Single Dispense Undefined Offset Issue

I see.

Yes, I agree, I think it is more consistent. Because otherwise we multiply all arguments no_channel times except for the Container itself.
This should solve the auto-spacing of the channels issue?

Are you saying that this would also solve the other issue:

  • The abrupt change in Container (if not Well) offset definition wrt to center-center-bottom, rather than flb?

I don’t mind changing all my automation protocols to the new standard for Tubes / Troughs, but this was a very sudden change, and I think we should issue a warning for users.

With this change

  • ValueError: Minimum distance between two y positions is <9mm: should be resolved because it is now recognized that there is only one resource, and channels are centered within the trough on the x-axis, and equally spaced on the y-axis.
  • “the tips are now moving off by exactly well.get_size_y()/2” should be fixed because the center of the resource is removed from the default offset.
    However, I think perhaps a further change is needed because now offsets are wrt the default resource.centers(yn=n, zn=0). If we make the offsets wrt lfb, that is inconsistent with how we treat offsets in wells, where offsets are wrt ccb. Essentially, if you aspirate from as many containers as channels, the offset would be wrt the ccb, but if you aspirate with multiple channels from one container, the offset would be wrt lfb. Even if that is what we did before*, that does not seem good.

*this only happened before because you were specifying the single resource in a list, whereas the syntax in the tests was to pass it as a single resource. If you had passed it as a single resource, the offsets would already be wrt the equally-spaced y-centers.

1 Like

I am sorry for introducing such a confusion-enabling api. Highlights why this needs fixing, with everything always in lists.

There’s nothing to apologise for; we just have to polish it a bit and enforce standard ways of writing these commands
(e.g. this list of source containers, and the upcoming change to standardised units for lh.aspirate/dispense) :slight_smile:

what would you prefer for offsets in single-resource multi-channel aspirations?

I think to stay consistent with everything else:

Which is now updated to be with regards to (wrt) center-center-bottom (ccb).
A list of [Coordinate(x=0, y=y_offset, z=0) for y_offset in offset_list] seems the most consistent across all of PLR.

The slight oddity is that now we can’t simply say offset_list = list(range(4.5, resource.size_y, 9)) anymore because with regards to ccb rather than front-left-bottom (flb).
This means the coordinates have to be positive and negative split in the middle of offset_list.

I don’t mind adapting though, as long as we stick with the current status quo (which is what I believe you intended originally as wel).

And we can still get around the problem using

offset_list = list(range(4.5, resource.size_y-4.5, 9))

resource_y_center =  resource.size_y/2

[Coordinate(x=0, y=y_offset-resource_y_center , z=0) for y_offset in offset_list]
1 Like

Let’s see how far this takes us. I won’t update the code anymore for now / the equal y-split is the behavior for now.

1 Like

Wait, does this mean offsets are not supported on aspirate and dispense anymore with the latest update?

I just tried the above channel positioning that is required for the trough, and it suddenly spaced all channels with double the distance they are meant to be?

But without any offsets they do correctly auto-space across the y length of the trough - which is fine for an even number of channels but will crash if an odd number of channels is used.

wait no, they are supported. They are just wrt the default equally-spread positions.

Were you still using your old offsets where all these offsets are wrt the lfb?

No, they are now all in regards to ccb of the trough

they should be wrt this:


(n=2)


(n=3)

etc

Thank you for the nice visuals.

This is what happens when not given an offset argument, but when given the spacing appears roughly doubled?

Here you can also see very nicely how odd channel numbers will crash the central tip into the anti-splash wall of the trough.

How does the new auto-space ensure that each channel is at least 9mm apart from the next?

even when the offset is tiny, say Coordinate(x=0.1)?

“nice”

It’s not actually new, and it doesn’t ensure that. It is just checked in the backend (STAR).

I haven’t tried only a single channel because I needed to process all 8 samples at the same time.
In that case I set a y offset list that is 9mm apart from each other, and then it appeared roughly double-spaced from that.

So there is a possibility that the auto-spacing auto-generates a spacing list that does not comply with the machine’s minimal spacing requirement of 9mm?

The default spacing should already space them in a reasonable pattern (if trough.size_y > 9 *(8+1)). You would just need to slightly change the offsets to go around the anti-spash wall. Perhaps like y +5 for the first 4 channels, and -5 for the last 4?

Yes. If there is not enough space in the resource, resource.centers(yn=n, zn=0) will give things closer together. This is done on the LH level, and centers is independent of liquid handler actually. It seems appropriate to make this a check at the STAR level , the check is done before a command is generated so in no case will the machine crash as a result of this.

An aspiration from a Porvair 6x reservoir plate with 8-channels does not perform the auto-spacing correctly, even though the reservoir-well is wide enough (in the y-dimension).

File pylabrobot\liquid_handling\backends\hamilton\base.py:351, in HamiltonLiquidHandler._ops_to_fw_positions(self, ops, use_channels)
    349       continue
    350     if abs(y1 - y2) < 90:
--> 351       raise ValueError(f"Minimum distance between two y positions is <9mm: {y1}, {y2}"
    352                        f" (channel {channel_idx1} and {channel_idx2})")
    354 if len(ops) > self.num_channels:
    355   raise ValueError(f"Too many channels specified: {len(ops)} > {self.num_channels}")

ValueError: Minimum distance between two y positions is <9mm: 3321, 3242 (channel 0 and 1)

Normally, I would just add the offset in this case (and as I have until now) but because of this unknown issue of doubling of the spacings this is not an option.
I had to manually recover my samples and add the liquid myself.

>>> from pylabrobot.resources.porvair import Porvair_6_reservoir_47ml_Vb
>>> Porvair_6_reservoir_47ml_Vb(name="name").get_item("A1").centers(yn=2)
[Coordinate(x=8.4, y=23.6, z=21.25), Coordinate(x=8.4, y=47.2, z=21.25)]

at first sight it looks like there is enough space

Could you share code of something that is not working as expected, because I am slightly confused about the double-spacing issue.