Skip to content

Conversation

BioCam
Copy link
Contributor

@BioCam BioCam commented Mar 1, 2025

Hi everyone,

In this PR I've updated the incomplete and broken Plate.get_quadrant() method, to a version that is functional independent of the wellplate format and enables flexible quadrant definitions.

Problem statement

Currently, Plate.get_quadrant()...

  • is only functional for 384-wellplates
  • only enables "checkerboard" quadrants
  • only enables "row-major" Well extractions.

Background: Definition Standardization

As proposed and discussed in the PLR developer forum (Quadrant Definition Standardization Across PLR & Across Plate Formats), PLR defines quadrants based on the first well of the quadrant's relative position to the plate's origin:

250227_Moschner_explainer_universal_quadrant_ids

PR Content

Here, I create a new Plate.get_quadrant() method which enables versatile quadrant usage for any Plate with even rows and even columns:

A minimal code example for you to test and explore its functionality:

%load_ext autoreload
%autoreload 2

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.resources.hamilton import STARLetDeck

from pylabrobot.liquid_handling.backends import LiquidHandlerChatterboxBackend
backend = LiquidHandlerChatterboxBackend()

lh = LiquidHandler(backend=backend, deck=STARLetDeck())

await lh.setup()

from pylabrobot.resources import (
    PLT_CAR_L5AC_A00,
    Cor_6_wellplate_16800ul_Fb,
    CellVis_24_wellplate_3600uL_Fb,
    Thermo_TS_96_wellplate_1200ul_Rb,
    Revvity_384_wellplate_28ul_Ub
)

plt_carrier_1 = PLT_CAR_L5AC_A00(name="plt_carrier_1")

plt_carrier_1[0] = example_6_wellplate = Cor_6_wellplate_16800ul_Fb(name="example_6_wellplate")
plt_carrier_1[2] = example_24_wellplate = CellVis_24_wellplate_3600uL_Fb(name="example_24_wellplate")
plt_carrier_1[3] = example_96_wellplate = Thermo_TS_96_wellplate_1200ul_Rb(name="example_96_wellplate")
plt_carrier_1[4] = example_384_wellplate = Revvity_384_wellplate_28ul_Ub(name="example_384_wellplate")

lh.deck.assign_child_resource(plt_carrier_1, rails=7)

Examples:
->

example_6_wellplate.get_quadrant(
    quadrant="tl",
) 

>>> ValueError: Both num_items_x and num_items_y must be even for quadrant selection,
>>> are self.num_items_x=3, self.num_items_y=2

By default, quadrant_type = "checkerboard", quadrant_internal_fill_order = "column-major":

example_24_wellplate.get_quadrant(
    quadrant="tl",
    # quadrant_type = "checkerboard",
    # quadrant_internal_fill_order = "column-major"
)

>>> [Well(name=example_24_wellplate_well_0_0, location=Coordinate(009.280, 063.800, 000.750), size_x=15.54, size_y=15.54, size_z=19, category=well),
>>> Well(name=example_24_wellplate_well_0_2, location=Coordinate(009.280, 025.200, 000.750), size_x=15.54, size_y=15.54, size_z=19, category=well),
>>> Well(name=example_24_wellplate_well_2_0, location=Coordinate(047.880, 063.800, 000.750), size_x=15.54, size_y=15.54, size_z=19, category=well),
>>> Well(name=example_24_wellplate_well_2_2, location=Coordinate(047.880, 025.200, 000.750), size_x=15.54, size_y=15.54, size_z=19, category=well),
>>> Well(name=example_24_wellplate_well_4_0, location=Coordinate(086.480, 063.800, 000.750), size_x=15.54, size_y=15.54, size_z=19, category=well),
>>> Well(name=example_24_wellplate_well_4_2, location=Coordinate(086.480, 025.200, 000.750), size_x=15.54, size_y=15.54, size_z=19, category=well)]

For simplified inspection, we can get_identifier() for each Well:

print([well.get_identifier() for well in example_96_wellplate.get_quadrant(
    quadrant="tl",
    # quadrant_type = "checkerboard",
    # quadrant_internal_fill_order = "column-major"
) ])

>>> ['A1', 'C1', 'E1', 'G1',
>>> 'A3', 'C3', 'E3', 'G3', 
>>> 'A5', 'C5', 'E5', 'G5', 
>>> 'A7', 'C7', 'E7', 'G7', 
>>> 'A9', 'C9', 'E9', 'G9', 
>>> 'A11', 'C11', 'E11', 'G11']

switching to "checkerboard" & "row-major":

print([well.get_identifier() for well in example_96_wellplate.get_quadrant(
    quadrant="tl",
    quadrant_type = "checkerboard", # Literal["block", "checkerboard"]
    quadrant_internal_fill_order = "row-major" # Literal["column-major", "row-major"]
) ])
>>> ['A1', 'A3', 'A5', 'A7', 'A9', 'A11', 
>>> 'C1', 'C3', 'C5', 'C7', 'C9', 'C11', 
>>> 'E1', 'E3', 'E5', 'E7', 'E9', 'E11', 
>>> 'G1', 'G3', 'G5', 'G7', 'G9', 'G11']

switching to "block" & "column-major":

print([well.get_identifier() for well in example_96_wellplate.get_quadrant(
    quadrant="tl",
    quadrant_type = "block", # Literal["block", "checkerboard"]
    quadrant_internal_fill_order = "column-major" # Literal["column-major", "row-major"]
) ])

>>> ['A1', 'B1', 'C1', 'D1', 
>>> 'A2', 'B2', 'C2', 'D2', 
>>> 'A3', 'B3', 'C3', 'D3', 
>>> 'A4', 'B4', 'C4', 'D4',
>>> 'A5', 'B5', 'C5', 'D5',
>>> 'A6', 'B6', 'C6', 'D6']

switching to "block" & "row-major":

print([well.get_identifier() for well in example_96_wellplate.get_quadrant(
    quadrant="tl",
    quadrant_type = "block", # Literal["block", "checkerboard"]
    quadrant_internal_fill_order = "row-major" # Literal["column-major", "row-major"]
) ])

>>> ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 
>>> 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 
>>> 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 
>>> 'D1', 'D2', 'D3', 'D4', 'D5', 'D6']

All possible quadrants are "top_left"/"tl", "top_right"/"tr", "bottom_left"/"bl", "bottom_right"/"br".

The same standardization scheme applies to any Plate with even rows and even columns.

@rickwierenga
Copy link
Member

so quick!

do you want to add a doc page with the contents of your PR + the gif? i like to make docs in notebooks so that people can download and play with it locally

@BioCam
Copy link
Contributor Author

BioCam commented Mar 1, 2025

so quick!

do you want to add a doc page with the contents of your PR + the gif? i like to make docs in notebooks so that people can download and play with it locally

Yes, that's a very good idea, and all the code is already made :)

I'll find some time over the weekend to write the docs page.

@rickwierenga
Copy link
Member

i think quadrant documentation should be moved from the 96 head (maybe keep one example there) to the plates page.

@rickwierenga rickwierenga merged commit dc61510 into PyLabRobot:main Mar 6, 2025
6 checks passed
@BioCam BioCam deleted the Plate-Quadrant-Standardization branch March 7, 2025 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants