Skills › Scientific Agent Skills › opentrons-integration › serial_dilution_template.py
serial_dilution_template.py
116 lines · skills/opentrons-integration/scripts/serial_dilution_template.py at 5f2bc03
1"""Full-plate 1:2 serial dilution on Opentrons Flex.
3Physical setup:
4- Put at least 12 mL diluent in reservoir A1.
5- Put 200 µL stock in every well of plate column 1.
6- Leave plate columns 2-12 empty.
7- Leave reservoir A12 empty for 800 µL of liquid waste.
9The protocol fills columns 2-12 with 100 µL diluent, serially transfers
10100 µL across the plate, and removes 100 µL from column 12 so every well
11finishes at 100 µL. Simulate, dry-run, and validate the assay before use.
12"""
14from opentrons import protocol_api
16metadata = {
17 "protocolName": "Flex 8-Channel Serial Dilution Template",
18 "author": "Customize before use",
19 "description": "Create eleven 1:2 dilution steps across a 96-well plate.",
20}
22requirements = {
23 "robotType": "Flex",
24 "apiLevel": "2.29",
25}
28def run(protocol: protocol_api.ProtocolContext) -> None:
29 """Perform the same serial dilution across all eight plate rows."""
30 tips_1 = protocol.load_labware(
31 "opentrons_flex_96_tiprack_200ul",
32 "D1",
33 label="Tips 1",
34 )
35 tips_2 = protocol.load_labware(
36 "opentrons_flex_96_tiprack_200ul",
37 "C1",
38 label="Tips 2",
39 )
40 reservoir = protocol.load_labware(
41 "nest_12_reservoir_15ml",
42 "D2",
43 label="Diluent and Liquid Waste Reservoir",
44 )
45 plate = protocol.load_labware(
46 "corning_96_wellplate_360ul_flat",
47 "C2",
48 label="Dilution Plate",
49 )
50 protocol.load_trash_bin("A3")
52 pipette = protocol.load_instrument(
53 instrument_name="flex_8channel_1000",
54 mount="left",
55 tip_racks=[tips_1, tips_2],
56 )
58 diluent = protocol.define_liquid(
59 name="Diluent",
60 description="Buffer or media used for the dilution series",
61 display_color="#9ECAE1",
62 )
63 stock = protocol.define_liquid(
64 name="Stock",
65 description="Starting material at the highest concentration",
66 display_color="#DE2D26",
67 )
68 reservoir.load_liquid(
69 wells=["A1"],
70 volume=12_000,
71 liquid=diluent,
72 )
73 plate.load_liquid(
74 wells=plate.columns()[0],
75 volume=200,
76 liquid=stock,
77 )
78 reservoir.load_empty(wells=["A12"])
80 # With a full 8-channel pipette, A-row wells address entire columns.
81 column_anchors = plate.rows()[0]
83 with protocol.group_steps(
84 name="Add Diluent",
85 description="Fill columns 2-12 with 100 µL diluent.",
86 ):
87 pipette.transfer(
88 volume=100,
89 source=reservoir["A1"],
90 dest=column_anchors[1:],
91 new_tip="once",
92 )
94 with protocol.group_steps(
95 name="Serial Dilution",
96 description="Transfer and mix through eleven 1:2 dilution steps.",
97 ):
98 pipette.transfer(
99 volume=100,
100 source=column_anchors[:11],
101 dest=column_anchors[1:],
102 mix_after=(3, 50),
103 new_tip="always",
104 )
106 with protocol.group_steps(
107 name="Equalize Final Volume",
108 description="Remove 100 µL from every well in column 12.",
109 ):
110 pipette.pick_up_tip()
111 pipette.aspirate(100, column_anchors[11])
112 # All eight channels dispense into one separate reservoir trough.
113 pipette.dispense(100, reservoir["A12"])
114 pipette.drop_tip()
116 protocol.comment("Serial dilution complete: columns 1-12 contain 100 µL per well.")