Modeling liquid handlers

I think it is worth throwing some infographics into this discussion to ensure we talk about the same things :slight_smile:

Yes: the firmware calls them “x-arm” while the user manuals call them “pipetting arm

Regarding arm vs gantry: they do mean slightly different things, to my understandings:

  • Arm - describes what it is physically: the moving structural member
  • Gantry - describes what it does functionally:
    positions the end-effector in the horizontal plane over the fixed deck, regardless of how many arms achieve that or what those arms are called

When compared to an FDM 3D-printer, a 4-gantry printer is one with 4 different toolheads, but importantly each toolhead has its own independent movement system (even with constraints):

This is different to e.g. the Bambu Lab H2D which has 2 nozzles but (1) they are on the same toolhead and (2) do not have independent movement actuation, making it a single-gantry system.

Based on this terminology the motion systems that bring end-effectors into position are defining what a “gantry” is → in the case of the STAR you could therefore argue that actually each channel has its own “gantry” because each channel brings its own y and z motors for independent movement in these dimensons.

→ I think x_arm might be the cleanest name for the physical sub-assembly in PLR?

(Please challenge me on these statements :slight_smile: )


Associated to this, I think it is worth mentioning that I’m obviously Hamilton-biased, based on experience.
Considering this, I believe Hamilton uses its own nomenclature, and, confusingly for everyone, uses the same terms for different meanings:

3 Likes

yes star.x_arms[0] will work, nice. Having components star.x_arms[0].iswap and star.x_arms[0].head96

but what do you think we should we call star.x_arms[0].X where X are the pip channels? (core question of discussion above)

like star.x_arms[0].X.aspirate(..., use_channels=[...]) (when not using the shorthand)

I thought the discussion was on modelling liquid handler :sweat_smile:

hence my effort on a “big picture” overview infographic

I would say star.x_arms[0].pip.aspirate()

simple and pretty universal based on the infographic from above

Should it be x_arm (singular) or x_arms (plural)?

I can see both points: the container indicates that there can be multiple elements but when addressing one we would always address a single one i.e. x_arm[0]

Also… is this not getting too long / complex a name for a simple command?
(just comparing star.x_arms[0].pip.aspirate() to lh.aspirate())

plural because it’s a list

too long

star.x_arms[0].pip.aspirate() is the most rigorous description and how data will be stored internally

star.pip will be a shorthand (see Modeling liquid handlers - #14 by rickwierenga)

realistically, most code will be functions like

async def some_liquid_handling_step(pip: PIP, some, parameters):
  await pip.aspirate
  await pip.dispense
  for ...

where PIP is any system that has independent channels (left hand side of your infographic)

1 Like

for the right hand side, they can realistically have these:

  • 1 channel
  • 8 channel
  • some other tools

but not independent channels on x gantry (by definition)

for the 1 channel case, I want to model those as PIP with num_channels=1) (PIP is the “variable channel” part of what is currently LiquidHandler.)

for the 8 channel case, use the then-existing head8 (analogous to head96).

for other tools, they will also have their own capability

Does this mean we call multi-channel pipettes “head” across PLR?
(apparently Agilent calls channels “barrels” and Hamilton calls them “stop disk”, and Hamilton calls a sub-assembly of the pipette channel … what an industry, I love it :joy: )

including ot2.head8 and prep.head8?

So we get…

  • star.pip.aspirate(use_channels=[0,2,3,6,7])
  • star.head96.aspirate()
  • star.head384.aspirate()
  • prep.pip.aspirate(use_channels=[0,1,2,3)
  • prep.head8.aspirate()

(all the shorthand versions)

It’s very nice :slight_smile:

4 Likes

that’s the working version yes, but in the case of star shorthand. you will be required to use the rigorous notation star.x_arms[0].pip.aspirate() when more than one x arm

how about rather than calling it pip we just call it head? this is nicely parallel with head8 and head96 etc.

this can become a common pattern in PLR: number suffix means a fixed number of channels

like I am working on dispensers right now, and the mantis would just have a diaphragm_dispenser, whereas the EL406 would have a syringe_dispenser8 to indicate it’s fixed 8 channels etc.