autoprotocol / autoprotocol-python

Python library for generating Autoprotocol
http://www.autoprotocol.org
Other
122 stars 40 forks source link

Protocol.dispense() ... are column subsets possible? #237

Open DNAS-bioDataSci opened 4 years ago

DNAS-bioDataSci commented 4 years ago

I'm exploring the reuse of autoprotocol in a client's dna-synthesis platform they've built from scratch.

When using Protocol.dispense() we must specify the set of targeted columns in a microplate. Same observed here autoprotocol.instruction.Dispense

Is there a way to specify the targeted dispense wells as the intersection of a column-set and a row-set? Or any arbitrary set of wells?

This is something the client's platform protocol requires (dispensing 4-rows at a time on a 96 well plate, but only in a subset of columns).

DNAS-bioDataSci commented 4 years ago

Resolved, thanks to support from Peter Lee. Given my specific example below, here's how we addressed it (along with a trailing feature request...):

Example 1: dispense to lower four rows, col 1 and 2 only:

    | 1   2   3   4   5   6   7   8   9   10  11  12 
----+---+---+---+---+---+---+---+---+---+---+---+---+
 A  |.   .   .   .   .   .   .   .   .   .   .   .   
 B  |.   .   .   .   .   .   .   .   .   .   .   .   
 C  |.   .   .   .   .   .   .   .   .   .   .   .   
 D  |.   .   .   .   .   .   .   .   .   .   .   .   
 E  |E1  E2  .   .   .   .   .   .   .   .   .   .   
 F  |F1  F2  .   .   .   .   .   .   .   .   .   .   
 G  |G1  G2  .   .   .   .   .   .   .   .   .   .   
 H  |H1  H2  .   .   .   .   .   .   .   .   .   .   
----+---+---+---+---+---+---+---+---+---+---+---+---+

Here's how to do it:

from autoprotocol.instruction import Dispense
from autoprotocol import Protocol, Unit

p = Protocol()
plate = p.ref("plate1", id=None, cont_type="96-flat", discard=True)

well_spacing_in_mm = Unit(9, "mm")  # microplate dependant measure between wells
row_offset = Dispense.builders.nozzle_position(position_y=5*well_spacing_in_mm)

p.dispense(plate, "water",
           Dispense.builders.columns(
               [Dispense.builders.column(0, "50:uL"),
               Dispense.builders.column(1, "50:uL")
               ]),
           shape={"rows":4, "columns":1, "format":"SBS96"},
           nozzle_position=row_offset)

p is rendered, in part:

...{
      "op": "dispense",
      "object": "plate1",
      "columns": [
        {
          "column": 0,
          "volume": "50:microliter"
        },
        {
          "column": 1,
          "volume": "50:microliter"
        }
      ],
      "reagent": "water",
      "step_size": "5:microliter",
      "nozzle_position": {
        "position_y": "45:millimeter"
      },
      "shape": {
        "rows": 4,
        "columns": 1,
        "format": "SBS96"
      }
   }...

While this gets the job done, using the nozzle_position's y-offset in millimeters to move to the 5th row seems to be too low level (i.e., too close to the end-effector raw requirement).

More user friendly could be: allow also a row_offset index (either a label E1 or integer 4) to be used in nozzle_position and move the well_spacing_in_mm to the plate definition.