diff --git a/docs/resources/library/vwr.md b/docs/resources/library/vwr.md index 62cae2f87ce..82edec8efbb 100644 --- a/docs/resources/library/vwr.md +++ b/docs/resources/library/vwr.md @@ -7,6 +7,7 @@ Company page: [Wikipedia](https://en.wikipedia.org/wiki/VWR_International) | Description | Image | PLR definition | |--------------------|--------------------|--------------------| | 'VWRReagentReservoirs25mL'
Part no.: 89094
[manufacturer website](https://us.vwr.com/store/product/4694822/vwr-disposable-pipetting-reservoirs)
Polystyrene Reservoirs | ![](img/vwr/VWRReagentReservoirs25mL.jpg) | `VWRReagentReservoirs25mL` | +| 'VWR_1_troughplate_195000uL_Ub'
Part no.: 77575-302
[manufacturer website](https://www.avantorsciences.com/us/en/product/47763965/vwr-multi-channel-polypropylene-reagent-reservoirs?isCatNumSearch=true&searchedCatalogNumber=77575-302)
Polypropylene multi-channel reagent reservoirs | ![](img/vwr/VWR_1_troughplate_195000uL_Ub.jpg) | `VWR_1_troughplate_195000uL_Ub` | ## Plates diff --git a/pylabrobot/liquid_handling/liquid_handler.py b/pylabrobot/liquid_handling/liquid_handler.py index ef194c5a3a8..b1af14d0288 100644 --- a/pylabrobot/liquid_handling/liquid_handler.py +++ b/pylabrobot/liquid_handling/liquid_handler.py @@ -836,7 +836,7 @@ def _check_containers(self, resources: Sequence[Resource]): @need_setup_finished async def aspirate( self, - resources: Sequence[Container], + resources: Union[Container, Sequence[Container]], vols: List[float], use_channels: Optional[List[int]] = None, flow_rates: Optional[List[Optional[float]]] = None, @@ -901,6 +901,9 @@ async def aspirate( ValueError: If all channels are `None`. """ + if isinstance(resources, Container): + resources = [resources] + self._log_command( "aspirate", resources=resources, @@ -946,6 +949,8 @@ async def aspirate( ("liquid_height", liquid_height), ("blow_out_air_volume", blow_out_air_volume), ]: + if n == "resources" and len(p) == 1: + continue if len(p) != len(use_channels): raise ValueError( f"Length of {n} must match length of use_channels: {len(p)} != {len(use_channels)}" @@ -1029,7 +1034,7 @@ async def aspirate( @need_setup_finished async def dispense( self, - resources: Sequence[Container], + resources: Union[Container, Sequence[Container]], vols: List[float], use_channels: Optional[List[int]] = None, flow_rates: Optional[List[Optional[float]]] = None, @@ -1092,6 +1097,9 @@ async def dispense( ValueError: If all channels are `None`. """ + if isinstance(resources, Container): + resources = [resources] + self._log_command( "dispense", resources=resources, @@ -1159,6 +1167,8 @@ async def dispense( ("liquid_height", liquid_height), ("blow_out_air_volume", blow_out_air_volume), ]: + if n == "resources" and len(p) == 1: + continue if len(p) != len(use_channels): raise ValueError( f"Length of {n} must match length of use_channels: {len(p)} != {len(use_channels)}" diff --git a/pylabrobot/liquid_handling/liquid_handler_tests.py b/pylabrobot/liquid_handling/liquid_handler_tests.py index d27eba719e2..80f22fefc66 100644 --- a/pylabrobot/liquid_handling/liquid_handler_tests.py +++ b/pylabrobot/liquid_handling/liquid_handler_tests.py @@ -29,6 +29,7 @@ ResourceNotFoundError, ResourceStack, TipRack, + VWR_1_trough_195000uL_Ub, nest_1_troughplate_195000uL_Vb, no_tip_tracking, set_tip_tracking, @@ -56,7 +57,9 @@ Drop, DropTipRack, GripDirection, + MultiHeadAspirationContainer, MultiHeadAspirationPlate, + MultiHeadDispenseContainer, MultiHeadDispensePlate, Pickup, ResourcePickup, @@ -634,6 +637,40 @@ async def test_aspirate_dispense96(self): ) ) + async def test_aspirate_dispense96_vwr_trough(self): + trough = VWR_1_trough_195000uL_Ub(name="vwr_trough") + self.deck.assign_child_resource(trough, location=Coordinate(300, 100, 0)) + + await self.lh.pick_up_tips96(self.tip_rack) + await self.lh.aspirate96(trough, volume=10) + await self.lh.dispense96(trough, volume=10) + + tips = [self.lh.head96[i].get_tip() for i in range(96)] + self.backend.aspirate96.assert_called_with( + aspiration=MultiHeadAspirationContainer( + container=trough, + offset=Coordinate.zero(), + tips=tips, + volume=10, + flow_rate=None, + liquid_height=None, + blow_out_air_volume=None, + mix=None, + ) + ) + self.backend.dispense96.assert_called_with( + dispense=MultiHeadDispenseContainer( + container=trough, + offset=Coordinate.zero(), + tips=tips, + volume=10, + flow_rate=None, + liquid_height=None, + blow_out_air_volume=None, + mix=None, + ) + ) + async def test_dispense96_with_quadrant_well_list(self): plate_384 = Revvity_384_wellplate_28ul_Ub(name="plate_384") self.deck.assign_child_resource(plate_384, location=Coordinate(400, 100, 0)) diff --git a/pylabrobot/resources/vwr/__init__.py b/pylabrobot/resources/vwr/__init__.py index fa3552c9330..d1f72871cd4 100644 --- a/pylabrobot/resources/vwr/__init__.py +++ b/pylabrobot/resources/vwr/__init__.py @@ -1,2 +1,2 @@ from .plates import VWR_1_troughplate_195000uL_Ub, VWR_96_wellplate_2mL_Vb -from .troughs import VWRReagentReservoirs25mL +from .troughs import VWR_1_trough_195000uL_Ub, VWRReagentReservoirs25mL diff --git a/pylabrobot/resources/vwr/troughs.py b/pylabrobot/resources/vwr/troughs.py index 252c9cc4348..379685f6067 100644 --- a/pylabrobot/resources/vwr/troughs.py +++ b/pylabrobot/resources/vwr/troughs.py @@ -1,4 +1,36 @@ -from pylabrobot.resources.trough import Trough +from pylabrobot.resources.height_volume_functions import ( + compute_height_from_volume_rectangle, + compute_volume_from_height_rectangle, +) +from pylabrobot.resources.trough import Trough, TroughBottomType + + +def VWR_1_trough_195000uL_Ub(name: str) -> Trough: + """VWR NA Cat. No. 77575-302""" + + inner_width = 127.76 - (14.38 - 8.9 / 2) * 2 + inner_length = 85.48 - (11.24 - 8.9 / 2) * 2 + + return Trough( + name=name, + size_x=127.76, # from spec + size_y=85.48, # from spec + size_z=31.4, # from spec + material_z_thickness=3.55, # from spec + max_volume=195000, # from spec 195 mL + model=VWR_1_trough_195000uL_Ub.__name__, + bottom_type=TroughBottomType.U, + compute_height_from_volume=lambda liquid_volume: compute_height_from_volume_rectangle( + liquid_volume, + inner_length, + inner_width, + ), + compute_volume_from_height=lambda liquid_height: compute_volume_from_height_rectangle( + liquid_height, + inner_length, + inner_width, + ), + ) def VWRReagentReservoirs25mL(name: str) -> Trough: