Skip to content

Commit 297c4fa

Browse files
committed
Merge remote-tracking branch 'origin/chore_release-pd-8.5.0' into merge-pd-8.5.0-alpha.3-into-edge
2 parents f833e59 + 31be425 commit 297c4fa

File tree

11 files changed

+568
-380
lines changed

11 files changed

+568
-380
lines changed

api/src/opentrons/protocol_api/core/engine/instrument.py

Lines changed: 142 additions & 248 deletions
Large diffs are not rendered by default.

api/src/opentrons/protocols/advanced_control/transfers/common.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import math
44
from typing import Iterable, Generator, Tuple, TypeVar, Literal, List, Union
55

6-
from opentrons.protocol_api._liquid_properties import LiquidHandlingPropertyByVolume
6+
from opentrons.protocol_api._liquid_properties import (
7+
LiquidHandlingPropertyByVolume,
8+
TransferProperties,
9+
)
710

811

912
class NoLiquidClassPropertyError(ValueError):
@@ -102,6 +105,37 @@ def _split_volume_equally(volume: float, max_volume: float) -> List[float]:
102105
return [volume / iterations for _ in range(iterations)]
103106

104107

108+
def get_sources_and_destinations_for_liquid_classes(
109+
volumes: List[float],
110+
max_volume: float,
111+
targets: Iterable[Target],
112+
transfer_properties: TransferProperties,
113+
is_multi_dispense: bool = False,
114+
) -> Generator[Tuple[float, "Target"], None, None]:
115+
"""Return a list of targets (wells or tuples of wells) and volumes for a liquid class transfer."""
116+
aspirate_air_gap_by_volume = transfer_properties.aspirate.retract.air_gap_by_volume
117+
if is_multi_dispense:
118+
assert transfer_properties.multi_dispense is not None
119+
disposal_vol_by_volume = transfer_properties.multi_dispense.disposal_by_volume
120+
conditioning_vol_by_volume = (
121+
transfer_properties.multi_dispense.conditioning_by_volume
122+
)
123+
return expand_for_volume_constraints_for_liquid_classes(
124+
volumes=volumes,
125+
targets=targets,
126+
max_volume=max_volume,
127+
air_gap=aspirate_air_gap_by_volume,
128+
disposal_vol=disposal_vol_by_volume,
129+
conditioning_vol=conditioning_vol_by_volume,
130+
)
131+
return expand_for_volume_constraints_for_liquid_classes(
132+
volumes=volumes,
133+
targets=targets,
134+
max_volume=max_volume,
135+
air_gap=aspirate_air_gap_by_volume,
136+
)
137+
138+
105139
def expand_for_volume_constraints_for_liquid_classes(
106140
volumes: Iterable[float],
107141
targets: Iterable[Target],

app/src/organisms/ODD/QuickTransferFlow/__tests__/utils/quickTransferStepCommands.test.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,9 @@ pipette.transfer_with_liquid_class(
242242
"offset": {"x": 0, "y": 0, "z": 0},
243243
"position_reference": "well-bottom",
244244
},
245-
"push_out_by_volume": [(0, 0)],
246245
"flow_rate_by_volume": [(0, 80)],
247246
"correction_by_volume": [(0, 0)],
248247
"delay": {"enabled": False},
249-
"mix": {"enabled": False},
250248
"submerge": {
251249
"delay": {"enabled": False},
252250
"start_position": {
@@ -264,6 +262,8 @@ pipette.transfer_with_liquid_class(
264262
"touch_tip": {"enabled": False},
265263
"blowout": {"enabled": True, "location": "source", "flow_rate": 50},
266264
},
265+
"push_out_by_volume": [(0, 0)],
266+
"mix": {"enabled": False},
267267
},
268268
}}},
269269
),
@@ -395,11 +395,9 @@ pipette.consolidate_with_liquid_class(
395395
"offset": {"x": 0, "y": 0, "z": 0},
396396
"position_reference": "well-bottom",
397397
},
398-
"push_out_by_volume": [(0, 0)],
399398
"flow_rate_by_volume": [(0, 80)],
400399
"correction_by_volume": [(0, 0)],
401400
"delay": {"enabled": False},
402-
"mix": {"enabled": False},
403401
"submerge": {
404402
"delay": {"enabled": False},
405403
"start_position": {
@@ -417,6 +415,8 @@ pipette.consolidate_with_liquid_class(
417415
"touch_tip": {"enabled": False},
418416
"blowout": {"enabled": True, "location": "trash", "flow_rate": 50},
419417
},
418+
"push_out_by_volume": [(0, 0)],
419+
"mix": {"enabled": False},
420420
},
421421
}}},
422422
),
@@ -549,11 +549,37 @@ pipette.distribute_with_liquid_class(
549549
"offset": {"x": 0, "y": 0, "z": 0},
550550
"position_reference": "well-bottom",
551551
},
552-
"push_out_by_volume": [(0, 0)],
553552
"flow_rate_by_volume": [(0, 80)],
554553
"correction_by_volume": [(0, 0)],
555554
"delay": {"enabled": False},
555+
"submerge": {
556+
"delay": {"enabled": False},
557+
"start_position": {
558+
"offset": {"x": 0, "y": 0, "z": 0},
559+
"position_reference": "well-bottom",
560+
},
561+
},
562+
"retract": {
563+
"air_gap_by_volume": [(0, 0)],
564+
"delay": {"enabled": False},
565+
"end_position": {
566+
"offset": {"x": 0, "y": 0, "z": 0},
567+
"position_reference": "well-bottom",
568+
},
569+
"touch_tip": {"enabled": False},
570+
"blowout": {"enabled": True, "location": "source", "flow_rate": 50},
571+
},
572+
"push_out_by_volume": [(0, 0)],
556573
"mix": {"enabled": False},
574+
},
575+
"multi_dispense": {
576+
"dispense_position": {
577+
"offset": {"x": 0, "y": 0, "z": 0},
578+
"position_reference": "well-bottom",
579+
},
580+
"flow_rate_by_volume": [(0, 80)],
581+
"correction_by_volume": [(0, 0)],
582+
"delay": {"enabled": False},
557583
"submerge": {
558584
"delay": {"enabled": False},
559585
"start_position": {
@@ -570,9 +596,9 @@ pipette.distribute_with_liquid_class(
570596
},
571597
"touch_tip": {"enabled": False},
572598
"blowout": {"enabled": True, "location": "source", "flow_rate": 50},
573-
"conditioning_by_volume": [(0, 0)],
574-
"disposal_by_volume": [(0, 0)],
575599
},
600+
"conditioning_by_volume": [(0, 0)],
601+
"disposal_by_volume": [(0, 0)],
576602
},
577603
}}},
578604
),

protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/FlowRateField.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -171,17 +171,18 @@ export function FlowRateField(props: FlowRateFieldProps): JSX.Element {
171171
const isFlowRateOutOfBounds =
172172
(maxFlowRate != null && flowRateNum > maxFlowRate) || flowRateNum < 0
173173

174-
let errorMessage: string | null = null
175-
if (
176-
(!isPristine && passThruProps.value !== undefined && flowRateNum === 0) ||
174+
const errorMessage =
175+
(passThruProps.value &&
176+
!isPristine &&
177+
passThruProps.value !== undefined &&
178+
flowRateNum === 0) ||
177179
isFlowRateOutOfBounds ||
178180
(isPristine && flowRateNum === 0)
179-
) {
180-
errorMessage = i18n.format(
181-
t('step_edit_form.field.flow_rate.error_out_of_bounds'),
182-
'capitalize'
183-
)
184-
}
181+
? i18n.format(
182+
t('step_edit_form.field.flow_rate.error_out_of_bounds'),
183+
'capitalize'
184+
)
185+
: passThruProps.errorToShow ?? null
185186

186187
useEffect(() => {
187188
if (isPristine && passThruProps.value == null) {
@@ -195,7 +196,7 @@ export function FlowRateField(props: FlowRateFieldProps): JSX.Element {
195196
padding={padding}
196197
type="number"
197198
setIsPristine={setIsPristine}
198-
errorToShow={maxFlowRate != null ? errorMessage : null}
199+
errorToShow={errorMessage}
199200
key={`${flowRateType}_FlowRateInput`}
200201
title={title}
201202
showTooltip={false}
@@ -209,7 +210,6 @@ export function FlowRateField(props: FlowRateFieldProps): JSX.Element {
209210
})
210211
: null
211212
}
212-
placeholder={String(defaultFlowRate)}
213213
/>
214214
)
215215
}

protocol-designer/src/steplist/formLevel/handleFormChange/utils.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
WATER_LIQUID_CLASS_NAME,
1414
} from '@opentrons/shared-data'
1515
import {
16+
DEST_WELL_BLOWOUT_DESTINATION,
1617
getTransferPlanAndReferenceVolumes,
1718
SOURCE_WELL_BLOWOUT_DESTINATION,
1819
} from '@opentrons/step-generation'
@@ -382,13 +383,20 @@ const getBlowoutFields = (args: {
382383
hardwareMaximumFlowRate = null,
383384
} = args
384385
const { enable, params } = blowout
385-
// transform location to additional equipment entity ID
386-
const transformedLocation =
387-
(params?.location === 'trash'
388-
? Object.values(additionalEquipmentEntities).find(
389-
({ name }) => name === 'trashBin' || name === 'wasteChute'
390-
)?.id
391-
: params?.location) ?? null
386+
387+
// transform location
388+
let transformedLocation: string | null = null
389+
if (params?.location === 'trash') {
390+
transformedLocation =
391+
Object.values(additionalEquipmentEntities).find(
392+
({ name }) => name === 'trashBin' || name === 'wasteChute'
393+
)?.id ?? null
394+
} else if (params?.location === 'source') {
395+
transformedLocation = SOURCE_WELL_BLOWOUT_DESTINATION
396+
} else if (params?.location === 'destination') {
397+
transformedLocation = DEST_WELL_BLOWOUT_DESTINATION
398+
}
399+
392400
const checkedFlowRate =
393401
params != null
394402
? min([

shared-data/js/helpers/linearInterpolate.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@ export const linearInterpolate = (
1717
left: number | null = null,
1818
right: number | null = null
1919
): number | null => {
20-
console.assert(
21-
interpolationPoints.length > 0,
22-
'At least one point required for interpolation'
23-
)
2420
if (interpolationPoints.length === 0) {
21+
console.warn('At least one point required for interpolation')
2522
return null
2623
}
2724
const sortedInterpolationPoints = interpolationPoints.sort((a, b) => {

step-generation/src/__tests__/consolidate.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,9 @@ mock_pipette.consolidate_with_liquid_class(
152152
},
153153
"dispense": {
154154
"dispense_position": {"offset": {"x": 0, "y": 0}},
155-
"push_out_by_volume": [(0, 0)],
156155
"flow_rate_by_volume": [(0, 2.2)],
157156
"correction_by_volume": [(0, 0)],
158157
"delay": {"enabled": False},
159-
"mix": {"enabled": False},
160158
"submerge": {
161159
"delay": {"enabled": False},
162160
"start_position": {"offset": {}},
@@ -168,6 +166,8 @@ mock_pipette.consolidate_with_liquid_class(
168166
"touch_tip": {"enabled": False},
169167
"blowout": {"enabled": False},
170168
},
169+
"push_out_by_volume": [(0, 0)],
170+
"mix": {"enabled": False},
171171
},
172172
}}},
173173
),
@@ -245,11 +245,9 @@ mock_pipette.consolidate_with_liquid_class(
245245
},
246246
"dispense": {
247247
"dispense_position": {"offset": {"x": 0, "y": 0}},
248-
"push_out_by_volume": [(0, 0)],
249248
"flow_rate_by_volume": [(0, 2.2)],
250249
"correction_by_volume": [(0, 0)],
251250
"delay": {"enabled": True, "duration": 12},
252-
"mix": {"enabled": True, "repetitions": 1, "volume": 36},
253251
"submerge": {
254252
"delay": {"enabled": False},
255253
"start_position": {"offset": {}},
@@ -261,6 +259,8 @@ mock_pipette.consolidate_with_liquid_class(
261259
"touch_tip": {"enabled": True, "z_offset": -3.4},
262260
"blowout": {"enabled": True, "location": "destination", "flow_rate": 2.3},
263261
},
262+
"push_out_by_volume": [(0, 0)],
263+
"mix": {"enabled": True, "repetitions": 1, "volume": 36},
264264
},
265265
}}},
266266
),
@@ -312,11 +312,9 @@ mock_pipette.consolidate_with_liquid_class(
312312
},
313313
"dispense": {
314314
"dispense_position": {"offset": {"x": 0, "y": 0}},
315-
"push_out_by_volume": [(0, 0)],
316315
"flow_rate_by_volume": [(0, 2.2)],
317316
"correction_by_volume": [(0, 0)],
318317
"delay": {"enabled": False},
319-
"mix": {"enabled": False},
320318
"submerge": {
321319
"delay": {"enabled": False},
322320
"start_position": {"offset": {}},
@@ -328,6 +326,8 @@ mock_pipette.consolidate_with_liquid_class(
328326
"touch_tip": {"enabled": False},
329327
"blowout": {"enabled": False},
330328
},
329+
"push_out_by_volume": [(0, 0)],
330+
"mix": {"enabled": False},
331331
},
332332
}}},
333333
),

step-generation/src/__tests__/distribute.test.ts

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,28 @@ mock_pipette.distribute_with_liquid_class(
188188
},
189189
"dispense": {
190190
"dispense_position": {"offset": {"x": 0, "y": 0, "z": 0}},
191-
"push_out_by_volume": [(0, 0)],
192191
"flow_rate_by_volume": [(0, 2.2)],
193192
"correction_by_volume": [(0, 0)],
194193
"delay": {"enabled": False},
194+
"submerge": {
195+
"delay": {"enabled": False},
196+
"start_position": {"offset": {}},
197+
},
198+
"retract": {
199+
"air_gap_by_volume": [(0, 0)],
200+
"delay": {"enabled": False},
201+
"end_position": {"offset": {}},
202+
"touch_tip": {"enabled": False},
203+
"blowout": {"enabled": True, "location": "trash", "flow_rate": 2.3},
204+
},
205+
"push_out_by_volume": [(0, 0)],
195206
"mix": {"enabled": False},
207+
},
208+
"multi_dispense": {
209+
"dispense_position": {"offset": {"x": 0, "y": 0, "z": 0}},
210+
"flow_rate_by_volume": [(0, 2.2)],
211+
"correction_by_volume": [(0, 0)],
212+
"delay": {"enabled": False},
196213
"submerge": {
197214
"delay": {"enabled": False},
198215
"start_position": {"offset": {}},
@@ -203,9 +220,9 @@ mock_pipette.distribute_with_liquid_class(
203220
"end_position": {"offset": {}},
204221
"touch_tip": {"enabled": False},
205222
"blowout": {"enabled": True, "location": "trash", "flow_rate": 2.3},
206-
"conditioning_by_volume": [(0, 0)],
207-
"disposal_by_volume": [(0, 60)],
208223
},
224+
"conditioning_by_volume": [(0, 0)],
225+
"disposal_by_volume": [(0, 60)],
209226
},
210227
}}},
211228
),
@@ -1000,11 +1017,28 @@ mock_pipette.distribute_with_liquid_class(
10001017
},
10011018
"dispense": {
10021019
"dispense_position": {"offset": {"x": 0, "y": 0, "z": 0}},
1003-
"push_out_by_volume": [(0, 0)],
10041020
"flow_rate_by_volume": [(0, 2.2)],
10051021
"correction_by_volume": [(0, 0)],
10061022
"delay": {"enabled": True, "duration": 12},
1023+
"submerge": {
1024+
"delay": {"enabled": False},
1025+
"start_position": {"offset": {}},
1026+
},
1027+
"retract": {
1028+
"air_gap_by_volume": [(0, 0)],
1029+
"delay": {"enabled": False},
1030+
"end_position": {"offset": {}},
1031+
"touch_tip": {"enabled": True, "z_offset": -3.4},
1032+
"blowout": {"enabled": True, "location": "trash", "flow_rate": 2.3},
1033+
},
1034+
"push_out_by_volume": [(0, 0)],
10071035
"mix": {"enabled": False},
1036+
},
1037+
"multi_dispense": {
1038+
"dispense_position": {"offset": {"x": 0, "y": 0, "z": 0}},
1039+
"flow_rate_by_volume": [(0, 2.2)],
1040+
"correction_by_volume": [(0, 0)],
1041+
"delay": {"enabled": True, "duration": 12},
10081042
"submerge": {
10091043
"delay": {"enabled": False},
10101044
"start_position": {"offset": {}},
@@ -1015,9 +1049,9 @@ mock_pipette.distribute_with_liquid_class(
10151049
"end_position": {"offset": {}},
10161050
"touch_tip": {"enabled": True, "z_offset": -3.4},
10171051
"blowout": {"enabled": True, "location": "trash", "flow_rate": 2.3},
1018-
"conditioning_by_volume": [(0, 10)],
1019-
"disposal_by_volume": [(0, 60)],
10201052
},
1053+
"conditioning_by_volume": [(0, 10)],
1054+
"disposal_by_volume": [(0, 60)],
10211055
},
10221056
}}},
10231057
),

0 commit comments

Comments
 (0)