This code's create function:
```from opentrons import robot, containers, instruments
path = 'C:\Users\calel_000\Libraries\Documents\Opentrons\Protocols\exampleGBF.csv'
sampletubes = 9 # can change this number here
harvest = 3 # can change this number
diluentnum = 16 # can change here
HCVconjwells = 16 # can change here
HIVconjwells = 4 # can change here
HCGconjwells = 4 # can change here
HBsAgconjwells = 8 # can change here
HCVsubswells = 16 # can change here
HIVsubswells = 4 # can change here
HCGsubswells = 4 # can change here
HBsAgsubswells = 8 # can change here
HCVstopwells = 16 # can change here
HIVstopwells = 4 # can change here
HCGstopwells = 4 # can change here
HBsAgstopwells = 8 # can change here
poolnum = 3
HBsAg_vol = 50
HCV_vol = 10
HCG_vol = 100
HIV_vol = 100
conjsub_vol = 50
rackrow = 4
containers.create(
'4x6_tuberackGBF', # name of you container
grid=(4, 6), # specify amount of (columns, rows)
spacing=(6, 10), # distances (mm) between each (column, row)
diameter=12, # diameter (mm) of each well on the plate
depth=75) # depth (mm) of each well on the plate
containers.create(
'3x4_vialrackGBF', # name of you container
grid=(3, 4), # specify amount of (columns, rows)
spacing=(10, 15), # distances (mm) between each (column, row)
diameter=20, # diameter (mm) of each well on the plate
depth=50) # depth (mm) of each well on the plate
HBsAgplate = containers.load('96-PCR-flat', 'A3') # was "deck 12" in protocol
HCVplate = containers.load('96-PCR-flat', 'B3') # was "deck 13" in protocol
HIVplate = containers.load('96-PCR-flat', 'C3') # was "deck 14" in protocol
HCGplate = containers.load('96-PCR-flat', 'D3') # was "deck 15" in protocol
samples1 = containers.load('4x6_tuberackGBF', 'A1', 'samples1') # was "deck 1"
samples2 = containers.load('4x6_tuberackGBF', 'A2', 'samples2') # was "deck 6"
reagents1 = containers.load('3x4_vialrackGBF', 'B1') # was "deck 2"
reagents2 = containers.load('3x4_vialrackGBF', 'C1') # was "deck 3"
tip200_rack1 = containers.load('tiprack-200ul', 'B2')
tip200_rack2 = containers.load('tiprack-200ul', 'C2')
trash = containers.load('point', 'D2')
p100 = instruments.Pipette(
axis='b',
name='p100single',
max_volume=200,
min_volume=10,
channels=1,
trash_container=trash,
tip_racks=[tip200_rack1, tip200_rack2])
diluent = reagents2.wells('B1')
conjugate1 = reagents1.wells('A1')
conjugate2 = reagents1.wells('A2')
conjugate3 = reagents1.wells('A3')
conjugate4 = reagents1.wells('A4')
substrate1_1 = reagents1.wells('B1')
substrate1_2 = reagents1.wells('C1')
substrate2_1 = reagents1.wells('B2')
substrate2_2 = reagents1.wells('C2')
substrate3_1 = reagents1.wells('B3')
substrate3_2 = reagents1.wells('C3')
substrate4_1 = reagents1.wells('B4')
substrate4_2 = reagents1.wells('C4')
finalreagent = reagents2.wells('A1')
samples = []
for tube in range(0, harvest):
if tube < 6:
rack = samples1
elif tube < 12:
rack = samples2
elif tube < 18:
rack = samples3
samples.append(rack.wells(tube*rackrow, length=poolnum))
pooltubes = []
for tube in range(0, harvest):
if tube < 6:
rack = samples1
elif tube < 12:
rack = samples2
tube -= 6
elif tube < 18:
rack = samples3
tube -= 6
pooltubes.append(rack.wells(3 + rackrow*tube))
for tube in range(0, harvest):
p100.transfer(200, samples[tube], pooltubes[tube], new_tip='always')
p100.pick_up_tip()
p100.mix(3, 100, pooltubes[tube])
p100.drop_tip()
```
generates out of order json like this:
"4x6_tuberackGBF": {
"locations": {
"A2": {
"diameter": 12,
"x": 0,
"depth": 75,
"total-liquid-volume": 0,
"y": 10,
"z": 0
},
"B2": {
"diameter": 12,
"x": 6,
"depth": 75,
"total-liquid-volume": 0,
"y": 10,
"z": 0
},
"C3": {
"diameter": 12,
"x": 12,
"depth": 75,
"total-liquid-volume": 0,
"y": 20,
"z": 0
},
"C4": {
"diameter": 12,
"x": 12,
"depth": 75,
"total-liquid-volume": 0,
"y": 30,
"z": 0
},
"A3": {
"diameter": 12,
"x": 0,
"depth": 75,
"total-liquid-volume": 0,
"y": 20,
"z": 0
},
"A5": {
"diameter": 12,
"x": 0,
"depth": 75,
"total-liquid-volume": 0,
"y": 40,
"z": 0
},
"D6": {
"diameter": 12,
"x": 18,
"depth": 75,
"total-liquid-volume": 0,
"y": 50,
"z": 0
},
"C6": {
"diameter": 12,
"x": 12,
"depth": 75,
"total-liquid-volume": 0,
"y": 50,
"z": 0
},
"B1": {
"diameter": 12,
"x": 6,
"depth": 75,
"total-liquid-volume": 0,
"y": 0,
"z": 0
},
"A4": {
"diameter": 12,
"x": 0,
"depth": 75,
"total-liquid-volume": 0,
"y": 30,
"z": 0
},
"C5": {
"diameter": 12,
"x": 12,
"depth": 75,
"total-liquid-volume": 0,
"y": 40,
"z": 0
},
"C1": {
"diameter": 12,
"x": 12,
"depth": 75,
"total-liquid-volume": 0,
"y": 0,
"z": 0
},
"D3": {
"diameter": 12,
"x": 18,
"depth": 75,
"total-liquid-volume": 0,
"y": 20,
"z": 0
},
"B6": {
"diameter": 12,
"x": 6,
"depth": 75,
"total-liquid-volume": 0,
"y": 50,
"z": 0
},
"B3": {
"diameter": 12,
"x": 6,
"depth": 75,
"total-liquid-volume": 0,
"y": 20,
"z": 0
},
"C2": {
"diameter": 12,
"x": 12,
"depth": 75,
"total-liquid-volume": 0,
"y": 10,
"z": 0
},
"D5": {
"diameter": 12,
"x": 18,
"depth": 75,
"total-liquid-volume": 0,
"y": 40,
"z": 0
},
"D1": {
"diameter": 12,
"x": 18,
"depth": 75,
"total-liquid-volume": 0,
"y": 0,
"z": 0
},
"D4": {
"diameter": 12,
"x": 18,
"depth": 75,
"total-liquid-volume": 0,
"y": 30,
"z": 0
},
"A1": {
"diameter": 12,
"x": 0,
"depth": 75,
"total-liquid-volume": 0,
"y": 0,
"z": 0
},
"B5": {
"diameter": 12,
"x": 6,
"depth": 75,
"total-liquid-volume": 0,
"y": 40,
"z": 0
},
"D2": {
"diameter": 12,
"x": 18,
"depth": 75,
"total-liquid-volume": 0,
"y": 10,
"z": 0
},
"A6": {
"diameter": 12,
"x": 0,
"depth": 75,
"total-liquid-volume": 0,
"y": 50,
"z": 0
},
"B4": {
"diameter": 12,
"x": 6,
"depth": 75,
"total-liquid-volume": 0,
"y": 30,
"z": 0
}
}
},
For some reason, works normally in jupyter
@lmtgalhardo can you confirm python versions in both environments?
running Python 3.6.1 in jupyter, not sure how I would check what version of python the app uses but guessing 3 as well?
confirmed in Python 3.5.1, opentrons version '2.4.2+195.g5b792ce'
But the JSON dict is unordered anyway, things like my_container.wells() are ordered correctly, it's just uglier to edit to JSON.
I had an issue with a customer where the wells were not functioning normally becasue of the json: if I said transfer to well 'A1', it would transfer to whichever well the unordered json had listed first. If I said transfer to well 'B1', it would transfer to whatever well the unordered json had as second, etc
Shown:

Above my output from jupyter, wells should be accessed in order A1 to D1, B1 to D1, C1 to D1

Shown above is a screenshot from a customer (testing here produced similar results) where the wells being accessed were the corresponding wells in that position of the unordered json. So well "A1" turned into well D1, well "D1" turned into well D4, etc
The quick ugly fix for this to copy and paste this function into your protocol:
def create_container_instance(name, grid, spacing, diameter, depth,
volume=0, slot=None, label=None):
from opentrons import robot
from opentrons.containers.placeable import Container, Well
if slot is None:
raise RuntimeError('"slot" argument is required.')
if label is None:
label = name
columns, rows = grid
col_spacing, row_spacing = spacing
custom_container = Container()
well_properties = {
'type': 'custom',
'diameter': diameter,
'height': depth,
'total-liquid-volume': volume
}
for r in range(rows):
for c in range(columns):
well = Well(properties=well_properties)
well_name = chr(c + ord('A')) + str(1 + r)
coordinates = (c * col_spacing, r * row_spacing, 0)
custom_container.add(well, well_name, coordinates)
# if a container is added to Deck AFTER a Pipette, the Pipette's
# Calibrator must update to include all children of Deck
for _, instr in robot.get_instruments():
if hasattr(instr, 'update_calibrator'):
instr.update_calibrator()
custom_container.properties['type'] = name
custom_container.get_name = lambda: label
# add to robot deck
robot.deck[slot].add(custom_container, label)
return custom_container
After copying that function into your protocol, use it like this:
plate1 = create_container_instance(
'2x3_plate', # name of you container
grid=(2, 3), # specify amount of (columns, rows)
spacing=(38.4, 38.4), # distances (mm) between each (column, row)
diameter=19, # diameter (mm) of each well on the plate
depth=8,
slot='A1'
) # depth (mm) of each well on the plate
plate2 = create_container_instance(
'2x3_plate', # name of your container
grid=(2, 3), # specify amount of (columns, rows)
spacing=(38.4, 38.4), # distances (mm) between each (column, row)
diameter=19, # diameter (mm) of each well on the plate
depth=8, # depth (mm) of each well on the plate
slot='A2') # slot on robot to load container into
These container instances will function just like a the normal container instance returned from containers.load:
p1000 = instruments.Pipette(
axis='b',
name='p1000',
max_volume=1000,
min_volume=100,
channels=1,
)
# plate1 and plate2 are just normal containers now
p1000.transfer(5, plate1.wells(0, to=10), plate2.wells(0, to=10))
I just ran into this issue today. What the robot thought was Well 0 in a custom container was actually Well C2, well 1 was B5, etc. There really was no apparent pattern, other than that it was consistent across multiple instances of the container on the same deck.
I worked around it by getting the robot to map out which wells it was using. Then I created a dictionary to map the usual well numbers (0,1,2,3 ...) to the numbers the robot understood.
BTW, this affects calibration, too. If the robot thinks Well C2 is first, you have to calibrate the whole slot to the bottom of C2, not A1.
This bug may already be fixed in v3, but if it isn't, it will definitely be fixed by the Update Labware milestone, because the new labware definitions (unlike the old ones) do not rely on the order of keys in JSON dictionaries, which are inherently unordered structures.
This ticket is closed or superseded accordingly