-
Notifications
You must be signed in to change notification settings - Fork 62
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
Create a distribute feature #176
Comments
@BioCam is the existing |
I am just reading through the async def transfer(
self,
source: Well,
targets: List[Well],
source_vol: Optional[float] = None,
ratios: Optional[List[float]] = None,
target_vols: Optional[List[float]] = None,
aspiration_flow_rate: Optional[float] = None,
dispense_flow_rates: Optional[Union[float, List[Optional[float]]]] = None,
**backend_kwargs
):
"""Transfer liquid from one well to another.
Examples:
Transfer 50 uL of liquid from the first well to the second well:
>>> lh.transfer(plate["A1"], plate["B1"], source_vol=50)
Transfer 80 uL of liquid from the first well equally to the first column:
>>> lh.transfer(plate["A1"], plate["A1:H1"], source_vol=80)
Transfer 60 uL of liquid from the first well in a 1:2 ratio to 2 other wells:
>>> lh.transfer(plate["A1"], plate["B1:C1"], source_vol=60, ratios=[2, 1])
Transfer arbitrary volumes to the first column:
>>> lh.transfer(plate["A1"], plate["A1:H1"], target_vols=[3, 1, 4, 1, 5, 9, 6, 2])
Args:
source: The source well.
targets: The target wells.
source_vol: The volume to transfer from the source well.
ratios: The ratios to use when transferring liquid to the target wells. If not specified, then
the volumes will be distributed equally.
target_vols: The volumes to transfer to the target wells. If specified, `source_vols` and
`ratios` must be `None`.
aspiration_flow_rate: The flow rate to use when aspirating, in ul/s. If `None`, the backend
default will be used.
dispense_flow_rates: The flow rates to use when dispensing, in ul/s. If `None`, the backend
default will be used. Either a single flow rate for all channels, or a list of flow rates,
one for each target well.
Raises:
RuntimeError: If the setup has not been run. See :meth:`~LiquidHandler.setup`.
"""
# Deprecation check for single values
if isinstance(targets, Well):
raise NotImplementedError("Single target is deprecated, use a list of targets.")
if isinstance(dispense_flow_rates, numbers.Rational):
raise NotImplementedError("Single dispense flow rate is deprecated, use a list of flow rates")
if target_vols is not None:
if ratios is not None:
raise TypeError("Cannot specify ratios and target_vols at the same time")
if source_vol is not None:
raise TypeError("Cannot specify source_vol and target_vols at the same time")
else:
if source_vol is None:
raise TypeError("Must specify either source_vol or target_vols")
if ratios is None:
ratios = [1] * len(targets)
target_vols = [source_vol * r / sum(ratios) for r in ratios]
await self.aspirate(
resources=[source],
vols=[sum(target_vols)],
flow_rates=aspiration_flow_rate,
**backend_kwargs)
for target, vol in zip(targets, target_vols):
await self.dispense(
resources=[target],
vols=[vol],
flow_rates=dispense_flow_rates,
use_channels=[0],
**backend_kwargs) But for complex functionality I think we should be inspired by Opentrons approach and build some Hamilton wisdom into it and top it up with a bit genuine innovation by us: Opentrons cleanly distinguishes between their I believe Opentrons is right to have lh.transfer(source_plate["A1", "B1", "E2", "H2", "D4", "F4", "A10", "G12"], destination_plate["A1:H1"],vols=[80, 40, 50, 100, 30, 50, 80, 75]) This by itself is already adding functionality to Hamilton machines that the hardware and firmware would have no problems with, but that I could not find in GUI-based proprietary software, no matter how much I searched. But, this is very different from a lh.distribute(water_trough, dest_matrix = [destination_plate["A1:H1"]], vols_matrix=[[80, 40, 50, 100, 30, 50, 80, 75]], use_channels=[0]) I am proposing a couple of things simultaneously in this little line here:
A simple showcase to highlight the power I imagine this to have: We all perform normalizations, no secret here. Let's say you have a 96-well plate of protein extractions one needs to normalise. There is no reason to transfer all these volumes of the same buffer to an empty well in separate transfers, This is where PLR's new It would be vastly faster than anything currently available and totally in the realm of y-independent channel systems ... the only thing that's missing is a flexible-enough, detailed-enough, computational design-empowered software = PLR :) Normalisation is just one application, I can see use cases like inoculations, experimental setup construction and more. This function should also have arguments like This is a big vision for a PLR method imo but I am proposing it because I believe it to be incredibly powerful, and a way to set PLR instantly apart and showcase what programmationally-designed liquid handling can do. To be clear, I don't believe the information regarding channel parameter settings to make such a function accurate and reliable is given with the current clinging to "liquid classes", but I'm sure we can figure out a better way using the full set of aspiration and dispensation arguments ;) |
I will make this |
hahaha 🤣 I thought you might like it |
I know how much fun it will be to actually build this function, so I won't take this from you @rickwierenga (plus, I've got a ton of work in my day job and other PLR dev tasks) but how about we have a meeting solely discussing how to implement this function in the next couple of day? For example, we must absolutely polish PS. I did warn you that once PLR is more open to complex, more "user-focused" features I will let a lot of ideas loose :P |
Sure, what time?
This has always been the goal: front ends exist to compose complex functions out of easy-to-implement atomic commands. (obviously while maintaining granular control.) From the paper: "Composition of the unit operations, such as the discard operation (drop tip in trash) and the transfer operations (combined aspirate and dispense), are performed at the LiquidHandler level and above". So excited we are nearing a stage where we can productively implement composite commands more than simple |
@rickwierenga, I'm available today until 15:00 UCT+1 or on Wednesday? :)
I completely agree 🚀 |
sorry to delay, but Wednesday is a lot better for me |
I know, let's do Wednesday during the day at 11:00 UCT+1? |
Perfect! |
like https://docs.opentrons.com/v2/new_protocol_api.html#opentrons.protocol_api.InstrumentContext.distribute
Can lift from https://github.com/rickwierenga/pylabrobot-art-studio, where this is already implemented. Just need to make it nice and general
The text was updated successfully, but these errors were encountered: