Skip to content

OT2 'reservoirs' #149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def _is_python_3_10():
def _mock_define(lw):
return {
"data": {
"definitionUri": f"lw['namespace']/{lw['metadata']['displayName']}/1"
"definitionUri": f'lw["namespace"]/{lw["metadata"]["displayName"]}/1'
}
}

Expand Down
4 changes: 2 additions & 2 deletions pylabrobot/machines/backends/machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ def deserialize(cls, data: dict):
class_name = data.pop("type")
subclass = find_subclass(class_name, cls=cls)
if subclass is None:
raise ValueError(f"Could not find subclass with name '{data['type']}'")
raise ValueError(f'Could not find subclass with name "{data["type"]}"')
if issubclass(subclass, ABCMeta):
raise ValueError(f"Subclass with name '{data['type']}' is abstract")
raise ValueError(f'Subclass with name "{data["type"]}" is abstract')
assert issubclass(subclass, cls)
return subclass(**data)
2 changes: 1 addition & 1 deletion pylabrobot/plate_reading/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def find_subclass(cls: Type[PlateReaderBackend], name: str) -> \

subclass = find_subclass(cls, data["type"])
if subclass is None:
raise ValueError(f"Could not find subclass with name {data['type']}")
raise ValueError(f'Could not find subclass with name {data["type"]}')

del data["type"]
return subclass(**data)
2 changes: 2 additions & 0 deletions pylabrobot/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
from .petri_dish import PetriDish, PetriDishHolder
from .plate import Plate, Lid, Well
from .powder import Powder
from .reservoir import Reservoir
from .resource import Resource
from .tip_rack import TipRack, TipSpot
from .trash import Trash
from .trough import Trough
from .tube import Tube
from .tube_rack import TubeRack

Expand Down
112 changes: 56 additions & 56 deletions pylabrobot/resources/ml_star/mfx_carriers.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,56 @@
""" ML Star MultiFleX (MFX) carriers """
# pylint: disable=empty-docstring
# pylint: disable=invalid-name
# pylint: disable=line-too-long
from pylabrobot.resources.carrier import (
MFXCarrier,
Coordinate,
create_homogeneous_carrier_sites
)
def MFX_CAR_L5_base(name: str) -> MFXCarrier:
""" Hamilton cat. no.: 188039
Labware carrier base for up to 5 Multiflex Modules
"""
return MFXCarrier(
name=name,
size_x=135.0,
size_y=497.0,
size_z=18.195,
sites=create_homogeneous_carrier_sites([
Coordinate(0.0, 5.0, 18.195),
Coordinate(0.0, 101.0, 18.195),
Coordinate(0.0, 197.0, 18.195),
Coordinate(0.0, 293.0, 18.195),
Coordinate(0.0, 389.0, 18.195)
],
site_size_x=135.0,
site_size_y=94.0,
),
model="MFX_CAR_L5_base"
)
def PLT_CAR_L4_SHAKER(name: str) -> MFXCarrier:
""" Hamilton cat. no.: 187001
Template carrier with 4 positions for Hamilton Heater Shaker.
Occupies 7 tracks (7T). Can be screwed onto the deck.
"""
return MFXCarrier(
name=name,
size_x=157.5,
size_y=497.0,
size_z=8.0,
sites=create_homogeneous_carrier_sites([
Coordinate(6.0, 2, 8.0), # not tested, interpolated Coordinate
Coordinate(6.0, 123, 8.0), # not tested, interpolated Coordinate
Coordinate(6.0, 244.0, 8.0), # tested using Hamilton_HC
Coordinate(6.0, 365.0, 8.0), # tested using Hamilton_HS
],
site_size_x=145.5,
site_size_y=104.0,
),
model="PLT_CAR_L4_SHAKER"
)
""" ML Star MultiFleX (MFX) carriers """

# pylint: disable=empty-docstring
# pylint: disable=invalid-name
# pylint: disable=line-too-long

from pylabrobot.resources.carrier import (
MFXCarrier,
Coordinate,
create_homogeneous_carrier_sites
)


def MFX_CAR_L5_base(name: str) -> MFXCarrier:
""" Hamilton cat. no.: 188039
Labware carrier base for up to 5 Multiflex Modules
"""
return MFXCarrier(
name=name,
size_x=135.0,
size_y=497.0,
size_z=18.195,
sites=create_homogeneous_carrier_sites([
Coordinate(0.0, 5.0, 18.195),
Coordinate(0.0, 101.0, 18.195),
Coordinate(0.0, 197.0, 18.195),
Coordinate(0.0, 293.0, 18.195),
Coordinate(0.0, 389.0, 18.195)
],
site_size_x=135.0,
site_size_y=94.0,
),
model="MFX_CAR_L5_base"
)

def PLT_CAR_L4_SHAKER(name: str) -> MFXCarrier:
""" Hamilton cat. no.: 187001
Template carrier with 4 positions for Hamilton Heater Shaker.
Occupies 7 tracks (7T). Can be screwed onto the deck.
"""
return MFXCarrier(
name=name,
size_x=157.5,
size_y=497.0,
size_z=8.0,
sites=create_homogeneous_carrier_sites([
Coordinate(6.0, 2, 8.0), # not tested, interpolated Coordinate
Coordinate(6.0, 123, 8.0), # not tested, interpolated Coordinate
Coordinate(6.0, 244.0, 8.0), # tested using Hamilton_HC
Coordinate(6.0, 365.0, 8.0), # tested using Hamilton_HS
],
site_size_x=145.5,
site_size_y=104.0,
),
model="PLT_CAR_L4_SHAKER"
)
1 change: 1 addition & 0 deletions pylabrobot/resources/opentrons/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .deck import OTDeck
from .load import load_opentrons_resource, load_shared_opentrons_resource
from .plates import *
from .reservoirs import *
from .tip_racks import *
from .tube_racks import *
from .module import OTModule
48 changes: 42 additions & 6 deletions pylabrobot/resources/opentrons/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@

from pylabrobot.resources.coordinate import Coordinate
from pylabrobot.resources.plate import Plate
from pylabrobot.resources.reservoir import Reservoir
from pylabrobot.resources.tip import Tip, TipCreator
from pylabrobot.resources.tip_rack import TipRack, TipSpot
from pylabrobot.resources.tube import Tube
from pylabrobot.resources.tube_rack import TubeRack
from pylabrobot.resources.well import Well
from pylabrobot.resources.well import Well, CrossSectionType


if TYPE_CHECKING:
Expand All @@ -27,7 +28,7 @@ class UnknownResourceType(Exception):

def ot_definition_to_resource(
data: "LabwareDefinition",
name: str) -> Union[Plate, TipRack, TubeRack]:
name: str) -> Union[Plate, TipRack, TubeRack, Reservoir]:
""" Convert an Opentrons definition file to a PyLabRobot resource file. """

if not USE_OT:
Expand All @@ -42,7 +43,8 @@ def ot_definition_to_resource(

tube_rack_display_cats = {"adapter", "aluminumBlock", "tubeRack"}

if display_category in ["wellPlate", "tipRack", "tubeRack", "adapter", "aluminumBlock"]:
if display_category in ["wellPlate", "tipRack", "tubeRack", "adapter", "aluminumBlock",
"reservoir"]:
items = data["ordering"]
wells: List[List[Union[TipSpot, Well, Tube]]] = [] # TODO: can we use TypeGuard?

Expand Down Expand Up @@ -72,13 +74,21 @@ def volume_from_name(name: str) -> float:
well_size_z = well_data["depth"]

location=Coordinate(x=well_data["x"], y=well_data["y"], z=well_data["z"])

if display_category == "wellPlate":

if well_data["shape"] == "rectangular":
cross_section_type = CrossSectionType.RECTANGLE
else:
cross_section_type = CrossSectionType.CIRCLE

well = Well(
name=item,
size_x=well_size_x,
size_y=well_size_y,
size_z=well_size_z,
max_volume=well_data["totalLiquidVolume"]
max_volume=well_data["totalLiquidVolume"],
cross_section_type=cross_section_type
)
well.location = location
wells[i].append(well)
Expand Down Expand Up @@ -113,6 +123,23 @@ def make_tip() -> Tip:
)
tube.location = location
wells[i].append(tube)
elif display_category == "reservoir":

if well_data["shape"] == "rectangular":
cross_section_type = CrossSectionType.RECTANGLE
else:
cross_section_type = CrossSectionType.CIRCLE

well = Well(
name=item,
size_x=well_size_x,
size_y=well_size_y,
size_z=well_size_z,
max_volume=well_data["totalLiquidVolume"],
cross_section_type=cross_section_type
)
well.location = location
wells[i].append(well)

if display_category == "wellPlate":
return Plate(
Expand Down Expand Up @@ -143,11 +170,20 @@ def make_tip() -> Tip:
items=cast(List[List[Tube]], wells),
model=data["metadata"]["displayName"]
)
if display_category == "reservoir":
return Reservoir(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
items=cast(List[List[Well]], wells),
model=data["metadata"]["displayName"]
)

raise UnknownResourceType(f"Unknown resource type '{display_category}'.")


def load_opentrons_resource(fn: str, name: str) -> Union[Plate, TipRack, TubeRack]:
def load_opentrons_resource(fn: str, name: str) -> Union[Plate, TipRack, TubeRack, Reservoir]:
""" Load an Opentrons resource from a file.

Args:
Expand Down Expand Up @@ -180,7 +216,7 @@ def load_shared_opentrons_resource(
definition: str,
name: str,
version: int = 1
) -> Union[Plate, TipRack, TubeRack]:
) -> Union[Plate, TipRack, TubeRack, Reservoir]:
""" Load an Opentrons resource from the shared Opentrons resource library.

See https://github.com/Opentrons/opentrons/tree/edge/shared-data.
Expand Down
1 change: 0 additions & 1 deletion pylabrobot/resources/opentrons/plates.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from pylabrobot.resources.opentrons.load import load_shared_opentrons_resource
from pylabrobot.resources.plate import Plate


def corning_384_wellplate_112ul_flat(name: str) -> Plate:
return cast(Plate, load_shared_opentrons_resource(
definition="corning_384_wellplate_112ul_flat",
Expand Down
46 changes: 46 additions & 0 deletions pylabrobot/resources/opentrons/reservoirs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from typing import cast

from pylabrobot.resources.opentrons.load import load_shared_opentrons_resource
from pylabrobot.resources.reservoir import Reservoir

def agilent_1_reservoir_290ml(name: str) -> Reservoir:
return cast(Reservoir, load_shared_opentrons_resource(
definition="agilent_1_reservoir_290ml",
name=name,
version=1
))

def axygen_1_reservoir_90ml(name: str) -> Reservoir:
return cast(Reservoir, load_shared_opentrons_resource(
definition="axygen_1_reservoir_90ml",
name=name,
version=1
))

def nest_12_reservoir_15ml(name: str) -> Reservoir:
return cast(Reservoir, load_shared_opentrons_resource(
definition="nest_12_reservoir_15ml",
name=name,
version=1
))

def nest_1_reservoir_195ml(name: str) -> Reservoir:
return cast(Reservoir, load_shared_opentrons_resource(
definition="nest_1_reservoir_195ml",
name=name,
version=1
))

def nest_1_reservoir_290ml(name: str) -> Reservoir:
return cast(Reservoir, load_shared_opentrons_resource(
definition="nest_1_reservoir_290ml",
name=name,
version=1
))

def usascientific_12_reservoir_22ml(name: str) -> Reservoir:
return cast(Reservoir, load_shared_opentrons_resource(
definition="usascientific_12_reservoir_22ml",
name=name,
version=1
))
Loading
Loading