Liquid Handling Software Toy Problem (Difficult)

Similar to my thread in scheduling going to post a series of toy challenge problems. The point of these challenge problem is pure and simple. Toy problems that we as automation engineers can speak about freely in an open forum. I mean some of the constraints I’m coming up with are ridiculous and we will never be scene on the job, but that’s not the point. The point is to have fun and solve your way out of tricky problems. (With all the free time you have as an automation engineer :rofl:)

Setup

Your company Awesome Therapeutics has two liquid handlers Iggy and Ziggy. They are configured like so.

Liquid Handlers

Both liquid handlers have the exact same deck layout it is only the hardware above that changes.

Iggy = {8-Channel Arm, Robot Arm Plate Gripper, Barcode Scanner, 20 Deck Positions}
Ziggy = {96-Channel Arm, Robot Arm Plate Gripper, Barcode Scanner, 20 Deck Positions}

  • :face_with_raised_eyebrow: The deck positions won’t really matter for this problem, but if you have a deck with more you can assume those deck spot are perpetually taken up by some device or need to be kept free for robot arm access.

Labware

Awesome Therapeutics is so awesome because they manage to do all their science using just two plate types. Every plate and tip box is and can assumed to be barcoded identifying itself and type.

Plates = {360uL Microtiter Plate V-Bottom, 300mL SBS Reservoir}
Tips = {~50uL Tips, ~300uL, ~1000uL}

  • :face_with_raised_eyebrow: Since vendors all sell different sizes of tips match the tips size closest to these three bins of tip type. The point is that there are three different types of tips. (Small, Medium, Large)

Workflows

Your lab only has two scientists Alice and Bob and they only do the following tasks.

Task 1 : From an arbitrary single column on 1-3 source plates aliquot 8uL to all the wells of a single destination plate. If there was only one source plate and one destination plate then this would replicate an arbitrary column of the source place 12 times across the destination plate. When there are multiple source columns Alice always mixes at the end.

Task 2 : Aspirate a volume from (8uL - 300uL) from a large basin and dispense it into all wells of 1-5 destination plates. All destination plates will have the same added volume at the end. Sometimes the destination plates have sample in them and sometimes the plates are empty. When they have sample Bob sometimes wants to mix.

Problem

Write one program that can perform the tasks for Alice and Bob and be run on both devices. (This may not be possible on one type of device due to the heavily restrictive programming environment, however you could write your own installer to get around this issue…)

You are only allowed one UI screen with at most 3 inputs - if you can use less :clap: cheers to you!

  • UI Inputs = {file_path :: String, continue_button :: bool, abort_button ::bool}

You are also allowed one or more confirmation screens for deck loading.

Awesome Therapeutics has an awesome software team as well so for any worklists for this solution you can assume the users will always download valid files to execute (no error checking on fields to make things simpler), however if the two tasks are solved with two different types of work-lists the program must be able to ingest both types since you only have one UI screen to accept a filepath, continue, and abort. The worklist file can be whatever file type you want. I’d say the meat and bones of this problem is finding the right data structure for this file to generalize both tasks.

:smiling_imp: HARD-MODE :smiling_imp:

There is a common issue that pops up in lab as well!

Awesome Therapeutics has had issues maintaining tip supply. Hard-coding tips has been a major issue in the past as when a tip type ran out on critical day the handlers were down as they had to be re-programed and re-tested and faith dropped on the robots. Somedays you only have one of the tip types but the means of production must go on! Because of this supply chain issue, Alice and Bob occasionally think they have put the correct tips on the deck when they were just mis-understood. Since the tip boxes are in fact barcoded though… there is really no reason they should need to know right?

Eliminate this common error programmatically!

5 Likes

For a solution in which one program can run both workflows, I would write a program that can take in a generic worklist structure with fields source_well, destination_well, volume. This is enough to handle the operations of the robot, we just have to include a rule for when to mix given the constraint in Task 1. Everything else is a pure computation problem to map from the task parameters to the worklist structure.

For the hard-mode question, I would reference a database to know what tips I’m using. Could it be that simple?

Edit: For the case where I don’t need to do variable volumes in Task 2 ie they all have the same starting volume then I use the 96-head for speed

1 Like

Sounds like a plan!

I’d probably yeah query some local db. But then with knowledge of the tip type from scanning, you know have to edit the deck programmatically.

Also what about installing and running on both machines? Somehow have to query what resources are on the liquid handler you’re installed on.

I’d probably yeah query some local db. But then with knowledge of the tip type from scanning, you know have to edit the deck programmatically.

Yes this is more down to the mechanics of the method software you’re using. Sometimes we point to two different layfiles, sometimes we point to overlapping resources on the same layfile (this is hard mode for people who try to understand the layfile). There’s also Venus libraries for modifying layfile properties at runtime. Probably the last one is what I would go with in this specific case.

In PyLabRobot it is typical to modify the deck state at runtime.

Also what about installing and running on both machines? Somehow have to query what resources are on the liquid handler you’re installed on.

Oh yeah good point. Well, Ziggy only works in a special case for Task 2. You’d want to have the choice of robot specified in the worklist and a notification screen to tell the operator which one.

Iggy and Ziggy can both do all the tasks. They’d just execute them differently.

Had a long flight so I created this demo method to meet the requirements.

Here’s how I approached it using Hamilton Venus. I didn’t have labware definitions or images for your specified labwares so I used similar Axygen labware instead. 450uL Vbottom and 240uL Trough.

Requirements:
2 different instrument configurations, 1 script.
In Venus you can query the firmware of the current instrument running the program for its Serial Number so you can have the same method behave differently depending on the instrument running it. I’m overwriting this value to be “Iggy” or “Ziggy.” If you had multiple Iggys and Ziggys you could have the method query for whether the instrument has a 96 Head installed, or how many 1mL channels are installed, but this meets the requirement. All of this hardware configuration information is retrievable by the method during run-time.


2 Tasks, 1 script.
Input file has a column for TaskNumber, and then has the input columns for each tasks specific parameters.

Ziggy handles Task 1 by first transferring a full rack of tips to a tip pickup adapter and then doing right side offset pickup of 1 column of tips and using that column of tips the same way as channels.
User selected tip size.
All 3 tip sizes are present on the deck, tip size to use comes from the input file. You can QC check whether the selected tip size can accommodate the desired volume, and either tell the user it can’t be done or split the volume down to do multiple transfers with the smaller tip size until desired volume is achieved. The instrument auto loads and scans the tip rack barcodes and will prompt if the wrong rack is loaded in the wrong location. I believe this meets the “Difficult” requirement.
Mixing
Whether to mix or not is controlled by the input file in both methods. Task 1 has an extra check that if (Mix = Yes, Current Plate is Last Plate, and Number Of Plates > 1) Then Mix, else don’t mix.

Iggy & Ziggy Task 1

Ziggy Task 2

Let me know if I missed any but overall pretty simple to do in Venus only about 160 lines of code.

4 Likes

@Stefan how could @KyleCook_GeNovu upload an installer or zip file so users can download his solution and play with it?

Discourse doesn’t support file sharing. I’m always in favor of Github for these types of files. Others have asked about hosting a file sharing platform for covering similar use cases, but I don’t know any that quite hit the mark out of the box. Github is really good for one-off applications like this though.

[Task 1 - FluentControl] - Video (Dropbox - Tecan.Core.Simulator.Visualizer_uWLmcR7jAZ.mp4 - Simplify your life)

Not sure I have completely understood the protocol so here is a quick mock-up of what I think the ask is for Task 1? Let me know if I am on the right track and will share the script?

The protocol can be run with both instruments, ie. a 8-span pipetting head or a 96 head. Just some parts are quicker with either head example final mixing would be quicker with a 96 head.

I have done this in FluentControl which allow dynamic adding of labware via user prompts so no need for a file input but file input of variables are also possible.

5 Likes

Ok looks like I am on the right-track, so here is the script breakdown. I have opened up the commands as much as possible but shout if you need to see more.

First section is user input and virtual setup of labware based on the variable SourcePlates (user input of number of source plates) and a static setup of the destinations plate:

The number of plates is setup by a loop defined as Plates which also determines the plate index and nest placement.

The next section is user prompts, via TouchTools, to select which wells from each source plates need to be transferred:

This is just a quick logic gate for each selection so each well offset can be defined and stored separately.

The final sections is actual business end of the pipetting. Has a bit of logic to select the correct well offset for each source plate. I used FluentControls Smart command for a quick pipetting structure, but you can also used loops if you prefer. Then a final logic gate for selective mixing cycle when source plates are greater than 1 and is only triggered in after the correct number of plates.

As always, there are multiple ways to can get to this solution, this was just the quickest way of the top of my head. I would normally spend time to have a variable the volumes and liquid class to trigger mixing or break the smart command into loops, as I am not the biggest fan of smart commands as I have used them with customers who then change their minds and ask for a more complex movement. Then the smart command is limiting so you have to switch to looped structures.

Enjoy!

3 Likes

Task 2 - FluentControl
(Dropbox - Tecan.Core.Simulator.Visualizer_BNPc0vzWTJ.mp4 - Simplify your life)

The first part of this script is using TouchTool to define a set of variables in one screen. Followed by setting up the worktable with the variable number of labware as a loop as before:


The next section grabs the adaptor head. Uses a sub-routine to trigger tip wafer removal if the 96 head pickup up tips from the 200ul nested stack. There is a small logic gate to select either the 500ul tip or the 200ul nested tips based on the user input of volume. then general pipetting in a plate number loop, before tips are dropped.


The final section is the mix if samples were in the plates. This is based on the user question at the start that reads out to a string variable “Yes” or “No”.


Let me know if you have any questions? Enjoy!

3 Likes

Could both liquid handlers perform these tasks as written or are these two separate methods to accomplish task1 and task2?

could we not consider a centralized form of protocol definition e.g. json
then standardize an instrument specific interface to read from centralized structure to dynamically build a protocol that is then performed
this harmonization would alleviate system/software specific constraints/“features” - but bring the ability to cross-operate protocols to instruments that work versus instruments that are down

@smohler I have written these as two different Fluents. One with an 8 head pippetting arm (FCA) and the other with 96 head arm (MCA). The commands are specific to each arm so the commands would need to be changed but both could do the job. It is just that an FCA can be slower at doing certain task than an MCA, especially mixing a whole plate or sample distribution due to the need to get fresh tip if there are individual samples involved.

However if they need to work from long tube racks and non-SBS well formats an FCA is the only arm that could work as and MCA cannot span tips.

Ideally with this task, I would suggest only using the MCA arm instrument or having both arms on one instrument. An MCA arm alone could do task 1 and 2 on one instrument the quickest. With both arms you could run both task 1 and 2 asynchrony or schedule the tasks.

Happy to share a script for task 1 and 2 with and MCA if you are interested, as the script structure will be slightly different to allow partial tip pickups.

Example of partial tip pickup: Haren Arulanantham, PhD on LinkedIn: #tecan #fluent #liquidhandling #labautomation #spacesaving #96quadrant384…

This 100% can be done on some liquid handlers platforms! I know some labs make their STARS deseralize and json payload to do just crazy cool run time stuff.

I can write most of this for the Tecan Fluent with 1 line in Fluent Control.

BOOM!

3 Likes

Sick solution method lol. 1.) listen to async event 2.) process async event.

Well there is still no real reason Iggy and Ziggy can’t both preform each others tasks. Part of the toy problem is also providing and opportunity for users who search this thread ways to achieve both with one device.

I like how @KyleCook_GeNovu got the STAR to preform both tasks independent of what liquid handler the program was installed on. I mean that’s pretty neat right.

And then you can run the rest of it from a Python script with these functions,

['_Fluent__client',
 '_Fluent__lastError',
 '_Fluent__progress',
 '_Fluent__state',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'add_labware',
 'aspirate',
 'close_method',
 'discover',
 'dispense',
 'drop_tips',
 'finish_execution',
 'get_all_runnable_methods',
 'get_fingers',
 'get_tips',
 'get_variable_names',
 'get_variable_value',
 'lastError',
 'pause_run',
 'prepare_method',
 'progress',
 'remove_labware',
 'resume_run',
 'run_method',
 'set_location',
 'set_variable_value',
 'shutdown',
 'start_fluent',
 'state',
 'stop_method',
 'subscribe_error',
 'subscribe_progress',
 'subscribe_state',
 'transfer_labware',
 'transfer_labware_back_to_base',
 'user_prompt',
 'variables']

easy peasy…

:rofl:

3 Likes

*wipes hands clean -“ Whelp my job’s done here. Problem solved”:rofl:

Also I checked my Fluent and I couldn’t find this open API block btw?

1 Like