@opentrons/shared-data 0.1.7-alpha.1 → 0.2.0-alpha.0
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.
- package/.coveragerc +4 -0
- package/.flake8 +31 -0
- package/Config.in +11 -0
- package/LICENSE +202 -0
- package/Makefile +13 -14
- package/Makefile-python.mk +134 -0
- package/buildroot.mk +35 -0
- package/command/index.ts +3 -1
- package/command/schemas/13.json +147 -315
- package/command/schemas/14.json +7254 -0
- package/command/types/index.ts +15 -4
- package/command/types/module.ts +24 -4
- package/command/types/setup.ts +5 -5
- package/command/types/support.ts +1 -0
- package/deck/definitions/5/ot2_short_trash.json +37 -0
- package/deck/definitions/5/ot2_standard.json +37 -0
- package/deck/definitions/5/ot3_standard.json +152 -51
- package/deck/schemas/5.json +46 -1
- package/labware/definitions/2/appliedbiosystemsmicroamp_384_wellplate_40ul/3.json +4737 -0
- package/labware/definitions/2/axygen_96_wellplate_500ul/1.json +1036 -0
- package/labware/definitions/2/axygen_96_wellplate_500ul/2.json +1159 -0
- package/labware/definitions/2/biorad_384_wellplate_50ul/4.json +4748 -0
- package/labware/definitions/2/biorad_96_wellplate_200ul_pcr/4.json +1187 -0
- package/labware/definitions/2/corning_12_wellplate_6.9ml_flat/4.json +210 -0
- package/labware/definitions/2/corning_24_wellplate_3.4ml_flat/4.json +342 -0
- package/labware/definitions/2/corning_48_wellplate_1.6ml_flat/5.json +610 -0
- package/labware/definitions/2/corning_6_wellplate_16.8ml_flat/4.json +134 -0
- package/labware/definitions/2/corning_96_wellplate_360ul_flat/4.json +1177 -0
- package/labware/definitions/2/corning_96_wellplate_360ul_lid/1.json +111 -0
- package/labware/definitions/2/ev_resin_tips_flex_96_labware/1.json +1 -1
- package/labware/definitions/2/ibidi_96_square_well_plate_300ul/1.json +1105 -0
- package/labware/definitions/2/ibidi_96_square_well_plate_300ul/2.json +1216 -0
- package/labware/definitions/2/ibidi_96_square_well_plate_300ul_lid/1.json +110 -0
- package/labware/definitions/2/nest_12_reservoir_22ml/1.json +232 -0
- package/labware/definitions/2/nest_24_wellplate_10.4ml/1.json +386 -0
- package/labware/definitions/2/nest_8_reservoir_22ml/1.json +162 -0
- package/labware/definitions/2/nest_96_wellplate_100ul_pcr_full_skirt/4.json +1166 -0
- package/labware/definitions/2/nest_96_wellplate_200ul_flat/4.json +1145 -0
- package/labware/definitions/2/nest_96_wellplate_2ml_deep/4.json +1261 -0
- package/labware/definitions/2/opentrons_96_wellplate_200ul_pcr_full_skirt/4.json +1174 -0
- package/labware/definitions/2/opentrons_flex_96_filtertiprack_1000ul/1.json +5 -0
- package/labware/definitions/2/opentrons_flex_96_filtertiprack_200ul/1.json +5 -0
- package/labware/definitions/2/opentrons_flex_96_filtertiprack_20ul/1.json +5 -0
- package/labware/definitions/2/opentrons_flex_96_filtertiprack_50ul/1.json +5 -0
- package/labware/definitions/2/opentrons_flex_96_tiprack_1000ul/1.json +5 -0
- package/labware/definitions/2/opentrons_flex_96_tiprack_200ul/1.json +5 -0
- package/labware/definitions/2/opentrons_flex_96_tiprack_20ul/1.json +5 -0
- package/labware/definitions/2/opentrons_flex_96_tiprack_50ul/1.json +5 -0
- package/labware/definitions/2/opentrons_flex_tiprack_lid/1.json +8 -8
- package/labware/definitions/2/opentrons_tough_pcr_auto_sealing_lid/1.json +8 -0
- package/labware/definitions/2/opentrons_tough_universal_lid/1.json +16 -1
- package/labware/definitions/2/smc_384_read_plate/1.json +4701 -0
- package/labware/definitions/2/smc_384_read_plate/2.json +5100 -0
- package/labware/definitions/2/thermoscientificnunc_96_wellplate_1300ul/3.json +1141 -0
- package/labware/definitions/2/thermoscientificnunc_96_wellplate_2000ul/3.json +1141 -0
- package/labware/definitions/2/usascientific_96_wellplate_2.4ml_deep/3.json +1243 -0
- package/labware/definitions/3/schema3test_96_well_aluminum_block/999.json +5 -2
- package/labware/definitions/3/schema3test_96_wellplate_200ul_pcr_full_skirt/999.json +15 -2
- package/labware/definitions/3/schema3test_aluminum_flat_bottom_plate/999.json +70 -0
- package/labware/definitions/3/schema3test_flex_96_tiprack_200ul/999.json +5 -9
- package/labware/definitions/3/schema3test_flex_96_tiprack_adapter/999.json +70 -0
- package/labware/definitions/3/schema3test_flex_tiprack_lid/999.json +146 -0
- package/labware/definitions/3/schema3test_tough_pcr_auto_sealing_lid/999.json +162 -0
- package/labware/definitions/3/schema3test_universal_flat_adapter/999.json +157 -0
- package/labware/fixtures/2/fixture_user_volumes_prototype.json +1137 -0
- package/labware/fixtures/3/fixture_2_plate.json +5 -2
- package/labware/fixtures/3/fixture_corning_24_plate.json +6 -3
- package/labware/fixtures/3/index.ts +4 -0
- package/labware/schemas/2.json +36 -1
- package/labware/schemas/3.json +57 -5
- package/lib/command/index.d.ts +39 -1
- package/lib/command/schemas/13.json +147 -315
- package/lib/command/schemas/14.json +7254 -0
- package/lib/command/types/index.d.ts +11 -3
- package/lib/command/types/module.d.ts +19 -6
- package/lib/command/types/setup.d.ts +5 -5
- package/lib/deck/definitions/5/ot2_short_trash.json +37 -0
- package/lib/deck/definitions/5/ot2_standard.json +37 -0
- package/lib/deck/definitions/5/ot3_standard.json +152 -51
- package/lib/deck/schemas/5.json +46 -1
- package/lib/index.mjs +240842 -193770
- package/lib/js/constants.d.ts +30 -9
- package/lib/js/fixtures.d.ts +77 -2
- package/lib/js/getLabware.d.ts +3 -3
- package/lib/js/helpers/__tests__/getMaxPushOutVolume.test.d.ts +1 -0
- package/lib/js/helpers/__tests__/getStackedItemsOnStartingDeck.test.d.ts +1 -0
- package/lib/js/helpers/__tests__/labwareSchemaShims.test.d.ts +1 -0
- package/lib/js/helpers/__tests__/pairsFromArray.test.d.ts +1 -0
- package/lib/js/helpers/__tests__/vectorMath.test.d.ts +1 -0
- package/lib/js/helpers/getFixedTrashLabwareDefinition.d.ts +2 -2
- package/lib/js/helpers/getLabwareDefURI.d.ts +3 -0
- package/lib/js/helpers/getLabwareDefinitionsByURIForProtocol.d.ts +6 -0
- package/lib/js/helpers/getLabwareInfoByLiquidId.d.ts +10 -0
- package/lib/js/helpers/getLiquidsByIdForLabware.d.ts +2 -0
- package/lib/js/helpers/getLoadedLabwareDefinitionsByUri.d.ts +2 -2
- package/lib/js/helpers/getMaxPushOutVolume.d.ts +2 -0
- package/lib/js/helpers/getStackedItemsOnStartingDeck.d.ts +50 -0
- package/lib/js/helpers/getStandardDeckViewLayerBlockList.d.ts +2 -0
- package/lib/js/helpers/getTipTypeFromTipRackDefinition.d.ts +2 -2
- package/lib/js/helpers/getWellFillFromLabwareId.d.ts +4 -0
- package/lib/js/helpers/getWellNamePerMultiTip.d.ts +3 -3
- package/lib/js/helpers/getWellTotalVolume.d.ts +2 -2
- package/lib/js/helpers/index.d.ts +22 -14
- package/lib/js/helpers/labwareInference.d.ts +3 -3
- package/lib/js/helpers/liquidClasses.d.ts +4 -2
- package/lib/js/helpers/matrixMath.d.ts +3 -0
- package/lib/js/helpers/pairsFromArray.d.ts +8 -0
- package/lib/js/helpers/parseProtocolCommands.d.ts +4 -4
- package/lib/js/helpers/positionMath.d.ts +127 -0
- package/lib/js/helpers/validateCustomLabwareHelper.d.ts +2 -2
- package/lib/js/helpers/vectorMath.d.ts +14 -0
- package/lib/js/helpers/wellSets.d.ts +4 -4
- package/lib/js/labware.d.ts +4 -3
- package/lib/js/liquidClasses.d.ts +4 -0
- package/lib/js/modules.d.ts +1 -1
- package/lib/js/types.d.ts +62 -13
- package/lib/labware/definitions/2/appliedbiosystemsmicroamp_384_wellplate_40ul/3.json +4737 -0
- package/lib/labware/definitions/2/axygen_96_wellplate_500ul/1.json +1036 -0
- package/lib/labware/definitions/2/axygen_96_wellplate_500ul/2.json +1159 -0
- package/lib/labware/definitions/2/biorad_384_wellplate_50ul/4.json +4748 -0
- package/lib/labware/definitions/2/biorad_96_wellplate_200ul_pcr/4.json +1187 -0
- package/lib/labware/definitions/2/corning_12_wellplate_6.9ml_flat/4.json +210 -0
- package/lib/labware/definitions/2/corning_24_wellplate_3.4ml_flat/4.json +342 -0
- package/lib/labware/definitions/2/corning_48_wellplate_1.6ml_flat/5.json +610 -0
- package/lib/labware/definitions/2/corning_6_wellplate_16.8ml_flat/4.json +134 -0
- package/lib/labware/definitions/2/corning_96_wellplate_360ul_flat/4.json +1177 -0
- package/lib/labware/definitions/2/corning_96_wellplate_360ul_lid/1.json +111 -0
- package/lib/labware/definitions/2/ev_resin_tips_flex_96_labware/1.json +1 -1
- package/lib/labware/definitions/2/ibidi_96_square_well_plate_300ul/1.json +1105 -0
- package/lib/labware/definitions/2/ibidi_96_square_well_plate_300ul/2.json +1216 -0
- package/lib/labware/definitions/2/ibidi_96_square_well_plate_300ul_lid/1.json +110 -0
- package/lib/labware/definitions/2/nest_12_reservoir_22ml/1.json +232 -0
- package/lib/labware/definitions/2/nest_24_wellplate_10.4ml/1.json +386 -0
- package/lib/labware/definitions/2/nest_8_reservoir_22ml/1.json +162 -0
- package/lib/labware/definitions/2/nest_96_wellplate_100ul_pcr_full_skirt/4.json +1166 -0
- package/lib/labware/definitions/2/nest_96_wellplate_200ul_flat/4.json +1145 -0
- package/lib/labware/definitions/2/nest_96_wellplate_2ml_deep/4.json +1261 -0
- package/lib/labware/definitions/2/opentrons_96_wellplate_200ul_pcr_full_skirt/4.json +1174 -0
- package/lib/labware/definitions/2/opentrons_flex_96_filtertiprack_1000ul/1.json +5 -0
- package/lib/labware/definitions/2/opentrons_flex_96_filtertiprack_200ul/1.json +5 -0
- package/lib/labware/definitions/2/opentrons_flex_96_filtertiprack_20ul/1.json +5 -0
- package/lib/labware/definitions/2/opentrons_flex_96_filtertiprack_50ul/1.json +5 -0
- package/lib/labware/definitions/2/opentrons_flex_96_tiprack_1000ul/1.json +5 -0
- package/lib/labware/definitions/2/opentrons_flex_96_tiprack_200ul/1.json +5 -0
- package/lib/labware/definitions/2/opentrons_flex_96_tiprack_20ul/1.json +5 -0
- package/lib/labware/definitions/2/opentrons_flex_96_tiprack_50ul/1.json +5 -0
- package/lib/labware/definitions/2/opentrons_flex_tiprack_lid/1.json +8 -8
- package/lib/labware/definitions/2/opentrons_tough_pcr_auto_sealing_lid/1.json +8 -0
- package/lib/labware/definitions/2/opentrons_tough_universal_lid/1.json +16 -1
- package/lib/labware/definitions/2/smc_384_read_plate/1.json +4701 -0
- package/lib/labware/definitions/2/smc_384_read_plate/2.json +5100 -0
- package/lib/labware/definitions/2/thermoscientificnunc_96_wellplate_1300ul/3.json +1141 -0
- package/lib/labware/definitions/2/thermoscientificnunc_96_wellplate_2000ul/3.json +1141 -0
- package/lib/labware/definitions/2/usascientific_96_wellplate_2.4ml_deep/3.json +1243 -0
- package/lib/labware/definitions/3/schema3test_96_well_aluminum_block/999.json +5 -2
- package/lib/labware/definitions/3/schema3test_96_wellplate_200ul_pcr_full_skirt/999.json +15 -2
- package/lib/labware/definitions/3/schema3test_aluminum_flat_bottom_plate/999.json +70 -0
- package/lib/labware/definitions/3/schema3test_flex_96_tiprack_200ul/999.json +5 -9
- package/lib/labware/definitions/3/schema3test_flex_96_tiprack_adapter/999.json +70 -0
- package/lib/labware/definitions/3/schema3test_flex_tiprack_lid/999.json +146 -0
- package/lib/labware/definitions/3/schema3test_tough_pcr_auto_sealing_lid/999.json +162 -0
- package/lib/labware/definitions/3/schema3test_universal_flat_adapter/999.json +157 -0
- package/lib/labware/fixtures/2/fixture_user_volumes_prototype.json +1137 -0
- package/lib/labware/fixtures/3/fixture_2_plate.json +5 -2
- package/lib/labware/fixtures/3/fixture_corning_24_plate.json +6 -3
- package/lib/labware/fixtures/3/index.d.ts +3 -0
- package/lib/labware/schemas/2.json +36 -1
- package/lib/labware/schemas/3.json +57 -5
- package/lib/liquid-class/definitions/1/{ethanol_80.json → ethanol_80/1.json} +545 -390
- package/lib/liquid-class/definitions/1/{glycerol_50.json → glycerol_50/1.json} +370 -215
- package/lib/liquid-class/definitions/1/{water.json → water/1.json} +151 -62
- package/lib/liquid-class/fixtures/1/fixture_glycerol50.json +1 -0
- package/lib/liquid-class/schemas/1.json +6 -0
- package/lib/module/definitions/3/absorbanceReaderV1.json +20 -151
- package/lib/module/definitions/3/flexStackerModuleV1.json +29 -389
- package/lib/module/definitions/3/heaterShakerModuleV1.json +16 -357
- package/lib/module/definitions/3/magneticBlockV1.json +18 -149
- package/lib/module/definitions/3/magneticModuleV1.json +16 -399
- package/lib/module/definitions/3/magneticModuleV2.json +16 -399
- package/lib/module/definitions/3/temperatureModuleV1.json +16 -153
- package/lib/module/definitions/3/temperatureModuleV2.json +16 -153
- package/lib/module/definitions/3/thermocyclerModuleV1.json +16 -1340
- package/lib/module/definitions/3/thermocyclerModuleV2.json +18 -3617
- package/lib/module/schemas/3.json +109 -12
- package/lib/pipette/definitions/1/pipetteModelSpecs.json +336 -0
- package/lib/pipette/definitions/2/general/ninety_six_channel/p200/3_2.json +660 -0
- package/lib/pipette/definitions/2/geometry/ninety_six_channel/p200/3_2.json +309 -0
- package/lib/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_1.json +28 -18
- package/lib/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_2.json +192 -0
- package/lib/protocol/types/schemaV8/index.d.ts +17 -1
- package/liquid-class/definitions/1/{ethanol_80.json → ethanol_80/1.json} +545 -390
- package/liquid-class/definitions/1/{glycerol_50.json → glycerol_50/1.json} +370 -215
- package/liquid-class/definitions/1/{water.json → water/1.json} +151 -62
- package/liquid-class/fixtures/1/fixture_glycerol50.json +1 -0
- package/liquid-class/schemas/1.json +6 -0
- package/module/definitions/3/absorbanceReaderV1.json +20 -151
- package/module/definitions/3/flexStackerModuleV1.json +29 -389
- package/module/definitions/3/heaterShakerModuleV1.json +16 -357
- package/module/definitions/3/magneticBlockV1.json +18 -149
- package/module/definitions/3/magneticModuleV1.json +16 -399
- package/module/definitions/3/magneticModuleV2.json +16 -399
- package/module/definitions/3/temperatureModuleV1.json +16 -153
- package/module/definitions/3/temperatureModuleV2.json +16 -153
- package/module/definitions/3/thermocyclerModuleV1.json +16 -1340
- package/module/definitions/3/thermocyclerModuleV2.json +18 -3617
- package/module/schemas/3.json +109 -12
- package/mypy.ini +22 -0
- package/package.json +1 -1
- package/pipette/definitions/1/pipetteModelSpecs.json +336 -0
- package/pipette/definitions/2/general/ninety_six_channel/p200/3_2.json +660 -0
- package/pipette/definitions/2/geometry/ninety_six_channel/p200/3_2.json +309 -0
- package/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_1.json +28 -18
- package/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_2.json +192 -0
- package/protocol/types/schemaV8/index.ts +29 -1
- package/pyproject.toml +119 -0
- package/pytest.ini +8 -0
- package/python_tests/__init__.py +1 -0
- package/python_tests/command/test_known_schema_ids.py +9 -0
- package/python_tests/conftest.py +108 -0
- package/python_tests/deck/__init__.py +0 -0
- package/python_tests/deck/test_list_names.py +12 -0
- package/python_tests/deck/test_position.py +101 -0
- package/python_tests/deck/test_typechecks.py +23 -0
- package/python_tests/errors/__init__.py +1 -0
- package/python_tests/errors/test_error_completeness.py +19 -0
- package/python_tests/errors/test_exceptions.py +74 -0
- package/python_tests/gripper/__init__.py +0 -0
- package/python_tests/gripper/test_definition.py +30 -0
- package/python_tests/gripper/test_load.py +19 -0
- package/python_tests/labware/__init__.py +13 -0
- package/python_tests/labware/test_typechecks.py +80 -0
- package/python_tests/liquid_classes/__init__.py +0 -0
- package/python_tests/liquid_classes/test_liquid_class_values.py +59 -0
- package/python_tests/liquid_classes/test_load.py +45 -0
- package/python_tests/liquid_classes/test_validations.py +192 -0
- package/python_tests/module/__init__.py +20 -0
- package/python_tests/module/test_load.py +49 -0
- package/python_tests/module/test_typechecks.py +26 -0
- package/python_tests/pipette/__init__.py +0 -0
- package/python_tests/pipette/test_load_data.py +109 -0
- package/python_tests/pipette/test_max_flow_rates_per_volume.py +112 -0
- package/python_tests/pipette/test_mutable_configurations.py +467 -0
- package/python_tests/pipette/test_pipette_definition.py +183 -0
- package/python_tests/pipette/test_pipette_load_name_conversions.py +244 -0
- package/python_tests/pipette/test_typechecks.py +47 -0
- package/python_tests/pipette/test_validate_schema.py +198 -0
- package/python_tests/protocol/__init__.py +7 -0
- package/python_tests/protocol/test_protocol_schema_v6.py +108 -0
- package/python_tests/protocol/test_protocol_schema_v7.py +35 -0
- package/python_tests/protocol/test_protocol_schema_v8.py +17 -0
- package/python_tests/protocol/test_typechecks.py +30 -0
- package/python_tests/robot/__init__.py +0 -0
- package/python_tests/robot/test_typechecks.py +13 -0
- package/lib/js/cypressUtils.d.ts +0 -1
- package/lib/js/helpers/getVectorDifference.d.ts +0 -2
- package/lib/js/helpers/getVectorSum.d.ts +0 -2
- /package/lib/js/{helpers/__tests__/getVectorDifference.test.d.ts → __tests__/fixtures.test.d.ts} +0 -0
- /package/lib/js/helpers/__tests__/{getVectorSum.test.d.ts → getLabwareInfoByLiquidId.test.d.ts} +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Tests for the default values in built-in liquid class definitions."""
|
|
2
|
+
import pytest
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from opentrons_shared_data import get_shared_data_root
|
|
6
|
+
from opentrons_shared_data.liquid_classes import load_definition
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _get_all_liquid_classes() -> List[str]:
|
|
10
|
+
return [
|
|
11
|
+
deffile.stem
|
|
12
|
+
for deffile in (
|
|
13
|
+
get_shared_data_root() / "liquid-class" / "definitions" / "1"
|
|
14
|
+
).iterdir()
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.parametrize("liquid_class_name", list(_get_all_liquid_classes()))
|
|
19
|
+
def test_correction_values_equal_each_other(liquid_class_name: str) -> None:
|
|
20
|
+
"""The correction volume values for each pipette/tiprack combo should all be equal to one another."""
|
|
21
|
+
liquid_class_def = load_definition(liquid_class_name, version=1, schema_version=1)
|
|
22
|
+
incorrect_combos = []
|
|
23
|
+
for by_pipette in liquid_class_def.byPipette:
|
|
24
|
+
for liquid_class_props in by_pipette.byTipType:
|
|
25
|
+
if liquid_class_props.multiDispense is None:
|
|
26
|
+
continue
|
|
27
|
+
aspirate_correction = liquid_class_props.aspirate.correctionByVolume
|
|
28
|
+
dispense_correction = liquid_class_props.singleDispense.correctionByVolume
|
|
29
|
+
multi_dispense_correction = (
|
|
30
|
+
liquid_class_props.multiDispense.correctionByVolume
|
|
31
|
+
)
|
|
32
|
+
if (
|
|
33
|
+
aspirate_correction != dispense_correction
|
|
34
|
+
or dispense_correction != multi_dispense_correction
|
|
35
|
+
):
|
|
36
|
+
incorrect_combos.append(
|
|
37
|
+
f"{by_pipette.pipetteModel} {liquid_class_props.tiprack}"
|
|
38
|
+
)
|
|
39
|
+
assert incorrect_combos == []
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.mark.parametrize("liquid_class_name", list(_get_all_liquid_classes()))
|
|
43
|
+
def test_flow_rates_equal_each_other(liquid_class_name: str) -> None:
|
|
44
|
+
"""The dispense flow rate values for each pipette/tiprack combo should all be equal to one another."""
|
|
45
|
+
liquid_class_def = load_definition(liquid_class_name, version=1, schema_version=1)
|
|
46
|
+
incorrect_combos = []
|
|
47
|
+
for by_pipette in liquid_class_def.byPipette:
|
|
48
|
+
for liquid_class_props in by_pipette.byTipType:
|
|
49
|
+
if liquid_class_props.multiDispense is None:
|
|
50
|
+
continue
|
|
51
|
+
dispense_flow_rates = liquid_class_props.singleDispense.flowRateByVolume
|
|
52
|
+
multi_dispense_flow_rates = (
|
|
53
|
+
liquid_class_props.multiDispense.flowRateByVolume
|
|
54
|
+
)
|
|
55
|
+
if dispense_flow_rates != multi_dispense_flow_rates:
|
|
56
|
+
incorrect_combos.append(
|
|
57
|
+
f"{by_pipette.pipetteModel} {liquid_class_props.tiprack}"
|
|
58
|
+
)
|
|
59
|
+
assert incorrect_combos == []
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from opentrons_shared_data import load_shared_data
|
|
4
|
+
from opentrons_shared_data.liquid_classes import load_definition, definition_exists
|
|
5
|
+
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
6
|
+
LiquidClassSchemaV1,
|
|
7
|
+
PositionReference,
|
|
8
|
+
Coordinate,
|
|
9
|
+
TipPosition,
|
|
10
|
+
Submerge,
|
|
11
|
+
DelayParams,
|
|
12
|
+
DelayProperties,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_load_liquid_class_schema_v1() -> None:
|
|
17
|
+
fixture_data = load_shared_data("liquid-class/definitions/1/water/1.json")
|
|
18
|
+
liquid_class_model = LiquidClassSchemaV1.model_validate_json(fixture_data)
|
|
19
|
+
liquid_class_def_from_model = json.loads(
|
|
20
|
+
liquid_class_model.model_dump_json(exclude_unset=True)
|
|
21
|
+
)
|
|
22
|
+
expected_liquid_class_def = json.loads(fixture_data)
|
|
23
|
+
assert liquid_class_def_from_model == expected_liquid_class_def
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_load_definition() -> None:
|
|
27
|
+
water_definition = load_definition(name="water", version=1, schema_version=1)
|
|
28
|
+
assert type(water_definition) is LiquidClassSchemaV1
|
|
29
|
+
assert water_definition.byPipette[0].pipetteModel == "flex_1channel_50"
|
|
30
|
+
assert water_definition.byPipette[0].byTipType[0].aspirate.submerge == Submerge(
|
|
31
|
+
startPosition=TipPosition(
|
|
32
|
+
positionReference=PositionReference.WELL_TOP,
|
|
33
|
+
offset=Coordinate(x=0, y=0, z=2),
|
|
34
|
+
),
|
|
35
|
+
speed=100,
|
|
36
|
+
delay=DelayProperties(enable=False, params=DelayParams(duration=0)),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_definition_exists() -> None:
|
|
41
|
+
"""Should return whether specified definition exists in shared data or not."""
|
|
42
|
+
assert definition_exists(name="water", version=1) is True
|
|
43
|
+
assert definition_exists(name="glycerol_50", version=1) is True
|
|
44
|
+
assert definition_exists(name="glycerol_oh_no", version=1) is False
|
|
45
|
+
assert definition_exists(name="glycerol_50", version=2) is False
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Tests that validate the built-in liquid class definitions."""
|
|
2
|
+
import pytest
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
|
|
5
|
+
from opentrons_shared_data import get_shared_data_root
|
|
6
|
+
from opentrons_shared_data.liquid_classes import load_definition
|
|
7
|
+
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
8
|
+
DelayProperties,
|
|
9
|
+
MixProperties,
|
|
10
|
+
TouchTipProperties,
|
|
11
|
+
BlowoutProperties,
|
|
12
|
+
BlowoutLocation,
|
|
13
|
+
AspirateProperties,
|
|
14
|
+
PositionReference,
|
|
15
|
+
SingleDispenseProperties,
|
|
16
|
+
MultiDispenseProperties,
|
|
17
|
+
TransferProperties,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_all_liquid_classes() -> List[str]:
|
|
22
|
+
return [
|
|
23
|
+
deffile.stem
|
|
24
|
+
for deffile in (
|
|
25
|
+
get_shared_data_root() / "liquid-class" / "definitions" / "1"
|
|
26
|
+
).iterdir()
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.mark.parametrize("liquid_class_name", list(_get_all_liquid_classes()))
|
|
31
|
+
def test_validate_unique_pipette_keys(liquid_class_name: str) -> None:
|
|
32
|
+
"""A liquid class definition should contain only one set of properties per pipette model."""
|
|
33
|
+
definition_dict = load_definition(liquid_class_name, version=1, schema_version=1)
|
|
34
|
+
pipette_models = [prop.pipetteModel for prop in definition_dict.byPipette]
|
|
35
|
+
assert len(pipette_models) == len(set(pipette_models))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize("liquid_class_name", list(_get_all_liquid_classes()))
|
|
39
|
+
def test_validate_unique_tip_keys(liquid_class_name: str) -> None:
|
|
40
|
+
"""A liquid class definition should contain only one set of properties per tip type."""
|
|
41
|
+
definition_dict = load_definition(liquid_class_name, version=1, schema_version=1)
|
|
42
|
+
|
|
43
|
+
for by_pip_prop in definition_dict.byPipette:
|
|
44
|
+
tipracks = [tip_prop.tiprack for tip_prop in by_pip_prop.byTipType]
|
|
45
|
+
assert len(tipracks) == len(set(tipracks))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_validate_delay_properties_dict() -> None:
|
|
49
|
+
"""Delay properties model validator should convert valid dict to DelayProperties."""
|
|
50
|
+
obj = DelayProperties.model_validate({"enable": True, "duration": 2})
|
|
51
|
+
assert isinstance(obj, DelayProperties)
|
|
52
|
+
assert obj.enable is True
|
|
53
|
+
assert obj.params.duration == 2 # type: ignore[union-attr]
|
|
54
|
+
|
|
55
|
+
with pytest.raises(
|
|
56
|
+
ValueError,
|
|
57
|
+
match="Delay properties should specify either duration or params, not both",
|
|
58
|
+
):
|
|
59
|
+
DelayProperties.model_validate(
|
|
60
|
+
{"enable": False, "params": {"duration": 2}, "duration": 2}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_validate_mix_properties_dict() -> None:
|
|
65
|
+
"""Mix properties model validator should convert valid dict to MixProperties."""
|
|
66
|
+
obj = MixProperties.model_validate({"enable": True, "repetitions": 2, "volume": 3})
|
|
67
|
+
assert isinstance(obj, MixProperties)
|
|
68
|
+
assert obj.enable is True
|
|
69
|
+
assert obj.params is not None
|
|
70
|
+
assert obj.params.repetitions == 2
|
|
71
|
+
assert obj.params.volume == 3
|
|
72
|
+
|
|
73
|
+
with pytest.raises(
|
|
74
|
+
ValueError,
|
|
75
|
+
match="either all of \\['repetitions', 'volume'\\] or 'params', not both",
|
|
76
|
+
):
|
|
77
|
+
MixProperties.model_validate(
|
|
78
|
+
{"enable": True, "repetitions": 2, "volume": 3, "params": {"foo": "bar"}}
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_validate_touchtip_properties_dict() -> None:
|
|
83
|
+
"""Touch tip properties model validator should convert valid dict to TouchTipProperties."""
|
|
84
|
+
obj = TouchTipProperties.model_validate(
|
|
85
|
+
{"enable": True, "z_offset": 2, "mm_from_edge": 3, "speed": 4}
|
|
86
|
+
)
|
|
87
|
+
assert isinstance(obj, TouchTipProperties)
|
|
88
|
+
assert obj.enable is True
|
|
89
|
+
assert obj.params is not None
|
|
90
|
+
assert obj.params.mmFromEdge == 3
|
|
91
|
+
assert obj.params.speed == 4
|
|
92
|
+
assert obj.params.zOffset == 2
|
|
93
|
+
|
|
94
|
+
with pytest.raises(
|
|
95
|
+
ValueError,
|
|
96
|
+
match="either all of \\['z_offset', 'mm_from_edge', 'speed'\\] or 'params', not both",
|
|
97
|
+
):
|
|
98
|
+
TouchTipProperties.model_validate(
|
|
99
|
+
{
|
|
100
|
+
"enable": True,
|
|
101
|
+
"z_offset": 2,
|
|
102
|
+
"mm_from_edge": 3,
|
|
103
|
+
"speed": 4,
|
|
104
|
+
"params": {"foo": "bar"},
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_validate_blowout_properties_dict() -> None:
|
|
110
|
+
"""Blowout properties model validator should convert valid dict to BlowoutProperties."""
|
|
111
|
+
obj = BlowoutProperties.model_validate(
|
|
112
|
+
{"enable": True, "location": "source", "flow_rate": 3}
|
|
113
|
+
)
|
|
114
|
+
assert isinstance(obj, BlowoutProperties)
|
|
115
|
+
assert obj.enable is True
|
|
116
|
+
assert obj.params is not None
|
|
117
|
+
assert obj.params.location == BlowoutLocation.SOURCE
|
|
118
|
+
assert obj.params.flowRate == 3
|
|
119
|
+
|
|
120
|
+
with pytest.raises(
|
|
121
|
+
ValueError,
|
|
122
|
+
match="either all of \\['location', 'flow_rate'\\] or 'params', not both",
|
|
123
|
+
):
|
|
124
|
+
BlowoutProperties.model_validate(
|
|
125
|
+
{
|
|
126
|
+
"enable": True,
|
|
127
|
+
"location": "source",
|
|
128
|
+
"flow_rate": 3,
|
|
129
|
+
"params": {"foo": "bar"},
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_validate_aspirate_properties_dict(
|
|
135
|
+
sample_transfer_properties_dict: Dict[str, Dict[str, Any]],
|
|
136
|
+
) -> None:
|
|
137
|
+
"""Aspirate properties model validator should convert valid dict to AspirateProperties."""
|
|
138
|
+
obj = AspirateProperties.model_validate(
|
|
139
|
+
sample_transfer_properties_dict["flex_1channel_50"][
|
|
140
|
+
"opentrons/opentrons_flex_96_tiprack_50ul/1"
|
|
141
|
+
]["aspirate"]
|
|
142
|
+
)
|
|
143
|
+
assert isinstance(obj, AspirateProperties)
|
|
144
|
+
assert obj.aspiratePosition.positionReference == PositionReference.WELL_BOTTOM
|
|
145
|
+
assert obj.mix.enable is False
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_validate_single_dispense_properties_dict(
|
|
149
|
+
sample_transfer_properties_dict: Dict[str, Dict[str, Any]],
|
|
150
|
+
) -> None:
|
|
151
|
+
"""Single dispense properties model validator should convert valid dict to SingleDispenseProperties."""
|
|
152
|
+
obj = SingleDispenseProperties.model_validate(
|
|
153
|
+
sample_transfer_properties_dict["flex_1channel_50"][
|
|
154
|
+
"opentrons/opentrons_flex_96_tiprack_50ul/1"
|
|
155
|
+
]["dispense"]
|
|
156
|
+
)
|
|
157
|
+
assert isinstance(obj, SingleDispenseProperties)
|
|
158
|
+
assert obj.dispensePosition.positionReference == PositionReference.WELL_BOTTOM
|
|
159
|
+
assert obj.mix.enable is False
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_validate_multi_dispense_properties_dict(
|
|
163
|
+
sample_transfer_properties_dict: Dict[str, Dict[str, Any]],
|
|
164
|
+
) -> None:
|
|
165
|
+
"""Multi dispense properties model validator should convert valid dict to MultiDispenseProperties."""
|
|
166
|
+
obj = MultiDispenseProperties.model_validate(
|
|
167
|
+
sample_transfer_properties_dict["flex_1channel_50"][
|
|
168
|
+
"opentrons/opentrons_flex_96_tiprack_50ul/1"
|
|
169
|
+
]["multi_dispense"]
|
|
170
|
+
)
|
|
171
|
+
assert isinstance(obj, MultiDispenseProperties)
|
|
172
|
+
assert obj.dispensePosition.positionReference == PositionReference.WELL_BOTTOM
|
|
173
|
+
assert obj.conditioningByVolume == [(0, 0)]
|
|
174
|
+
assert obj.disposalByVolume == [(0, 5)]
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_validate_transfer_properties_dict(
|
|
178
|
+
sample_transfer_properties_dict: Dict[str, Dict[str, Any]],
|
|
179
|
+
) -> None:
|
|
180
|
+
"""Transfer properties model validator should convert valid dict to TransferProperties."""
|
|
181
|
+
obj = TransferProperties.model_validate(
|
|
182
|
+
sample_transfer_properties_dict["flex_1channel_50"][
|
|
183
|
+
"opentrons/opentrons_flex_96_tiprack_50ul/1"
|
|
184
|
+
]
|
|
185
|
+
)
|
|
186
|
+
assert isinstance(obj, TransferProperties)
|
|
187
|
+
assert (
|
|
188
|
+
obj.aspirate.aspiratePosition.positionReference == PositionReference.WELL_BOTTOM
|
|
189
|
+
)
|
|
190
|
+
assert obj.singleDispense.pushOutByVolume == [(10.0, 7.0), (20.0, 10.0)]
|
|
191
|
+
assert obj.multiDispense.conditioningByVolume == [(0, 0)] # type: ignore[union-attr]
|
|
192
|
+
assert obj.multiDispense.disposalByVolume == [(0, 5)] # type: ignore[union-attr]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def list_v2_defs() -> List[str]:
|
|
6
|
+
return [
|
|
7
|
+
deffile.stem
|
|
8
|
+
for deffile in (
|
|
9
|
+
Path(__file__).parent / ".." / ".." / "module" / "definitions" / "2"
|
|
10
|
+
).iterdir()
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def list_v3_defs() -> List[str]:
|
|
15
|
+
return [
|
|
16
|
+
deffile.stem
|
|
17
|
+
for deffile in (
|
|
18
|
+
Path(__file__).parent / ".." / ".." / "module" / "definitions" / "3"
|
|
19
|
+
).iterdir()
|
|
20
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from opentrons_shared_data.module import (
|
|
5
|
+
load_definition,
|
|
6
|
+
load_schema,
|
|
7
|
+
ModuleNotFoundError,
|
|
8
|
+
)
|
|
9
|
+
from opentrons_shared_data import load_shared_data
|
|
10
|
+
|
|
11
|
+
from . import list_v2_defs, list_v3_defs
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.parametrize("def_name", list_v3_defs())
|
|
15
|
+
def test_load_v3_defs(def_name: str) -> None:
|
|
16
|
+
"""Test that all v3 definitions load correctly."""
|
|
17
|
+
assert load_definition("3", def_name) == json.loads(
|
|
18
|
+
load_shared_data(f"module/definitions/3/{def_name}.json")
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.mark.parametrize("def_name", list_v2_defs())
|
|
23
|
+
def test_load_v2_defs(def_name: str) -> None:
|
|
24
|
+
assert load_definition("2", def_name) == json.loads( # type: ignore [call-overload]
|
|
25
|
+
load_shared_data(f"module/definitions/2/{def_name}.json")
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.mark.parametrize("def_name", ["magdeck", "tempdeck", "thermocycler"])
|
|
30
|
+
def test_load_v1_defs(def_name: str) -> None:
|
|
31
|
+
assert (
|
|
32
|
+
load_definition("1", def_name)
|
|
33
|
+
== json.loads(load_shared_data("module/definitions/1.json"))[def_name]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_bad_module_name_throws() -> None:
|
|
38
|
+
with pytest.raises(ModuleNotFoundError):
|
|
39
|
+
load_definition("1", "alsjdag")
|
|
40
|
+
|
|
41
|
+
with pytest.raises(ModuleNotFoundError):
|
|
42
|
+
load_definition("2", "asdasda") # type: ignore[call-overload]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@pytest.mark.parametrize("schema_name", ["1", "2", "3"])
|
|
46
|
+
def test_load_schema(schema_name: str) -> None:
|
|
47
|
+
assert load_schema(schema_name) == json.loads( # type: ignore[arg-type]
|
|
48
|
+
load_shared_data(f"module/schemas/{schema_name}.json")
|
|
49
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import typeguard
|
|
3
|
+
|
|
4
|
+
from opentrons_shared_data import module
|
|
5
|
+
from opentrons_shared_data.module import types
|
|
6
|
+
|
|
7
|
+
from . import list_v2_defs, list_v3_defs
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.parametrize("defname", list_v3_defs())
|
|
11
|
+
def test_v3_definitions_match_types(defname: str) -> None:
|
|
12
|
+
"""Test that V3 module definitions match ModuleDefinitionV3."""
|
|
13
|
+
def_dict = module.load_definition("3", defname)
|
|
14
|
+
typeguard.check_type(def_dict, types.ModuleDefinitionV3)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.mark.parametrize("defname", list_v2_defs())
|
|
18
|
+
def test_v2_definitions_match_types(defname: str) -> None:
|
|
19
|
+
defdict = module.load_definition("2", defname) # type: ignore [call-overload]
|
|
20
|
+
typeguard.check_type(defdict, types.ModuleDefinitionV2)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.parametrize("defname", ["magdeck", "tempdeck", "thermocycler"])
|
|
24
|
+
def test_v1_definitions_match_types(defname: str) -> None:
|
|
25
|
+
defdict = module.load_definition("1", defname)
|
|
26
|
+
typeguard.check_type(defdict, types.ModuleDefinitionV1)
|
|
File without changes
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from typing import Dict, Any, cast
|
|
3
|
+
from opentrons_shared_data.pipette import (
|
|
4
|
+
load_data,
|
|
5
|
+
pipette_load_name_conversions,
|
|
6
|
+
types,
|
|
7
|
+
)
|
|
8
|
+
from opentrons_shared_data.pipette.types import (
|
|
9
|
+
PipetteChannelType,
|
|
10
|
+
PipetteModelType,
|
|
11
|
+
PipetteVersionType,
|
|
12
|
+
PipetteOEMType,
|
|
13
|
+
PipetteTipType,
|
|
14
|
+
Quirks,
|
|
15
|
+
LiquidClasses,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_load_pipette_definition() -> None:
|
|
20
|
+
pipette_config_one = load_data.load_definition(
|
|
21
|
+
PipetteModelType.p50,
|
|
22
|
+
PipetteChannelType.SINGLE_CHANNEL,
|
|
23
|
+
PipetteVersionType(major=3, minor=3),
|
|
24
|
+
PipetteOEMType.OT,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
assert pipette_config_one.channels == 1
|
|
28
|
+
assert pipette_config_one.pipette_type.value == "p50"
|
|
29
|
+
assert pipette_config_one.nozzle_offset == [-8.0, -22.0, -259.15]
|
|
30
|
+
|
|
31
|
+
assert (
|
|
32
|
+
pipette_config_one.liquid_properties[LiquidClasses.default]
|
|
33
|
+
.supported_tips[PipetteTipType.t50]
|
|
34
|
+
.default_aspirate_flowrate.default
|
|
35
|
+
== 8.0
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
assert pipette_config_one.quirks == []
|
|
39
|
+
pipette_config_two = load_data.load_definition(
|
|
40
|
+
PipetteModelType.p50,
|
|
41
|
+
PipetteChannelType.SINGLE_CHANNEL,
|
|
42
|
+
PipetteVersionType(major=1, minor=0),
|
|
43
|
+
PipetteOEMType.OT,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
assert pipette_config_two.channels == 1
|
|
47
|
+
assert pipette_config_two.pipette_type.value == "p50"
|
|
48
|
+
assert pipette_config_two.nozzle_offset == [0.0, 0.0, 25.0]
|
|
49
|
+
assert (
|
|
50
|
+
pipette_config_two.liquid_properties[LiquidClasses.default]
|
|
51
|
+
.supported_tips[PipetteTipType.t200]
|
|
52
|
+
.default_aspirate_flowrate.default
|
|
53
|
+
== 25.0
|
|
54
|
+
)
|
|
55
|
+
assert pipette_config_two.quirks == [Quirks.dropTipShake]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.mark.parametrize(
|
|
59
|
+
argnames=["key_spot_check", "value_spot_check"],
|
|
60
|
+
argvalues=[
|
|
61
|
+
["P1KSV10", "p1000_single_v1.0"],
|
|
62
|
+
["P1KHV33", "p1000_96_v3.3"],
|
|
63
|
+
["P20MV21", "p20_multi_v2.1"],
|
|
64
|
+
["P300MV10", "p300_multi_v1.0"],
|
|
65
|
+
["P3HMV14", "p300_multi_v1.4"],
|
|
66
|
+
],
|
|
67
|
+
)
|
|
68
|
+
def test_build_serial_number_lookup(key_spot_check: str, value_spot_check: str) -> None:
|
|
69
|
+
lookup_table = load_data.load_serial_lookup_table()
|
|
70
|
+
assert lookup_table[key_spot_check] == value_spot_check
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@pytest.mark.parametrize(
|
|
74
|
+
argnames=["pipette_model", "v1_configuration_changes"],
|
|
75
|
+
argvalues=[
|
|
76
|
+
["p300_multi_v1.4", {"max_volume": 200, "min_volume": 10}],
|
|
77
|
+
["p1000_96_v3.3", {"tip_length": 40, "min_volume": 300}],
|
|
78
|
+
],
|
|
79
|
+
)
|
|
80
|
+
def test_update_pipette_configuration(
|
|
81
|
+
pipette_model: str, v1_configuration_changes: Dict[str, Any]
|
|
82
|
+
) -> None:
|
|
83
|
+
|
|
84
|
+
liquid_class = LiquidClasses.default
|
|
85
|
+
model_name = pipette_load_name_conversions.convert_pipette_model(
|
|
86
|
+
cast(types.PipetteModel, pipette_model)
|
|
87
|
+
)
|
|
88
|
+
base_configurations = load_data.load_definition(
|
|
89
|
+
model_name.pipette_type,
|
|
90
|
+
model_name.pipette_channels,
|
|
91
|
+
model_name.pipette_version,
|
|
92
|
+
model_name.oem_type,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
updated_configurations = load_data.update_pipette_configuration(
|
|
96
|
+
base_configurations, v1_configuration_changes, liquid_class
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
updated_configurations_dict = updated_configurations.model_dump()
|
|
100
|
+
for k, v in v1_configuration_changes.items():
|
|
101
|
+
if k == "tip_length":
|
|
102
|
+
for i in updated_configurations_dict["liquid_properties"][liquid_class][
|
|
103
|
+
"supported_tips"
|
|
104
|
+
].values():
|
|
105
|
+
assert i["default_tip_length"] == v
|
|
106
|
+
else:
|
|
107
|
+
assert (
|
|
108
|
+
updated_configurations_dict["liquid_properties"][liquid_class][k] == v
|
|
109
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pytest
|
|
3
|
+
from typing import Iterator
|
|
4
|
+
from opentrons_shared_data import get_shared_data_root
|
|
5
|
+
from opentrons_shared_data.pipette.pipette_load_name_conversions import (
|
|
6
|
+
convert_pipette_model,
|
|
7
|
+
)
|
|
8
|
+
from opentrons_shared_data.pipette.load_data import load_definition
|
|
9
|
+
from opentrons_shared_data.pipette.ul_per_mm import piecewise_volume_conversion
|
|
10
|
+
|
|
11
|
+
from opentrons_shared_data.pipette.types import PipetteModel
|
|
12
|
+
from opentrons_shared_data.pipette.pipette_definition import (
|
|
13
|
+
ulPerMMDefinition,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
DEFAULT_MAX_SPEED_HIGH_THROUGHPUT_OT3_AXIS_KIND_P = 15
|
|
18
|
+
DEFAULT_MAX_SPEED_LOW_THROUGHPUT_OT3_AXIS_KIND_P = 70
|
|
19
|
+
B_MAX_SPEED = 40
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _get_plunger_max_speed(pipette_model: PipetteModel) -> float:
|
|
23
|
+
if "v2" in pipette_model:
|
|
24
|
+
return B_MAX_SPEED
|
|
25
|
+
else:
|
|
26
|
+
if "96" in pipette_model:
|
|
27
|
+
return DEFAULT_MAX_SPEED_HIGH_THROUGHPUT_OT3_AXIS_KIND_P
|
|
28
|
+
else:
|
|
29
|
+
return DEFAULT_MAX_SPEED_LOW_THROUGHPUT_OT3_AXIS_KIND_P
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _get_max_flow_rate_at_volume(
|
|
33
|
+
ul_per_mm_definition: ulPerMMDefinition,
|
|
34
|
+
pipette_model: PipetteModel,
|
|
35
|
+
volume: float,
|
|
36
|
+
) -> float:
|
|
37
|
+
max_speed = _get_plunger_max_speed(pipette_model)
|
|
38
|
+
map = list(ul_per_mm_definition.default.values())[-1]
|
|
39
|
+
ul_per_mm = piecewise_volume_conversion(volume, map)
|
|
40
|
+
return round(ul_per_mm * max_speed, 1)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_all_pipette_models() -> Iterator[PipetteModel]:
|
|
44
|
+
paths_to_validate = (
|
|
45
|
+
get_shared_data_root() / "pipette" / "definitions" / "2" / "liquid"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
_channel_model_str = {
|
|
49
|
+
"single_channel": "single",
|
|
50
|
+
"ninety_six_channel": "96",
|
|
51
|
+
"eight_channel": "multi",
|
|
52
|
+
"eight_channel_em": "multi_em",
|
|
53
|
+
}
|
|
54
|
+
assert os.listdir(paths_to_validate), "You have a path wrong"
|
|
55
|
+
for channel_dir in os.listdir(paths_to_validate):
|
|
56
|
+
for model_dir in os.listdir(paths_to_validate / channel_dir):
|
|
57
|
+
for liquid_file in os.listdir(paths_to_validate / channel_dir / model_dir):
|
|
58
|
+
for version_file in os.listdir(
|
|
59
|
+
paths_to_validate / channel_dir / model_dir / liquid_file
|
|
60
|
+
):
|
|
61
|
+
version_list = version_file.split(".json")[0].split("_")
|
|
62
|
+
built_model: PipetteModel = PipetteModel(
|
|
63
|
+
f"{model_dir}_{_channel_model_str[channel_dir]}_v{version_list[0]}.{version_list[1]}"
|
|
64
|
+
)
|
|
65
|
+
if version_list[0] != "1" and version_list[1] != "0":
|
|
66
|
+
yield built_model
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pytest.mark.parametrize("pipette", list(get_all_pipette_models()))
|
|
70
|
+
@pytest.mark.parametrize("action", ["aspirate", "dispense"])
|
|
71
|
+
def test_max_flow_rates_per_volume(pipette: PipetteModel, action: str) -> None:
|
|
72
|
+
"""Verify the max flow rate values for each pipette's supported tip is in range"""
|
|
73
|
+
pipette_model_version = convert_pipette_model(pipette)
|
|
74
|
+
definition = load_definition(
|
|
75
|
+
pipette_model_version.pipette_type,
|
|
76
|
+
pipette_model_version.pipette_channels,
|
|
77
|
+
pipette_model_version.pipette_version,
|
|
78
|
+
pipette_model_version.oem_type,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
pipette_model_version_str = f"{pipette_model_version}"
|
|
82
|
+
|
|
83
|
+
for liquid_name, liquid_properties in definition.liquid_properties.items():
|
|
84
|
+
for tip_type, supported_tip in liquid_properties.supported_tips.items():
|
|
85
|
+
|
|
86
|
+
"""TODO: the following models do not pass the asserts since the uiMaxFlowRate was raised
|
|
87
|
+
to match the default blowout and dispense flowRates. uiMaxFlowRate will be reevaluated
|
|
88
|
+
in the future."""
|
|
89
|
+
if not (
|
|
90
|
+
(
|
|
91
|
+
pipette_model_version_str
|
|
92
|
+
in {
|
|
93
|
+
"p50_single_v3.4",
|
|
94
|
+
"p50_single_v3.5",
|
|
95
|
+
"p50_single_v3.6",
|
|
96
|
+
"p50_multi_v3.5",
|
|
97
|
+
"p50_multi_v3.4",
|
|
98
|
+
}
|
|
99
|
+
and liquid_properties.min_volume == 5.0
|
|
100
|
+
)
|
|
101
|
+
or (
|
|
102
|
+
pipette_model_version_str
|
|
103
|
+
in {"p200_96_v3.0", "p200_96_v3.1", "p200_96_v3.2"}
|
|
104
|
+
and liquid_properties.min_volume == 0.5
|
|
105
|
+
)
|
|
106
|
+
):
|
|
107
|
+
assert supported_tip.ui_max_flow_rate <= _get_max_flow_rate_at_volume(
|
|
108
|
+
supported_tip.aspirate, pipette, liquid_properties.min_volume
|
|
109
|
+
)
|
|
110
|
+
assert supported_tip.ui_max_flow_rate <= _get_max_flow_rate_at_volume(
|
|
111
|
+
supported_tip.dispense, pipette, liquid_properties.min_volume
|
|
112
|
+
)
|