From 79ac1723d1ac4a203e7820263194b9d7c62cb776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramifarawi=E2=80=9D?= <“rami.farawi@opentrons.com”> Date: Mon, 1 Apr 2024 12:07:12 -0400 Subject: [PATCH] fix --- data/data/fields.csv | 1 + protoBuilds/3e0bef/3e0b3f.ot2.apiv2.py.json | 4 +- protoBuilds/spotsee/spotsee.ot2.apiv2.py.json | 14 +- protocols/3e0bef/3e0b3f.ot2.apiv2.py | 925 +++++++++--------- protocols/spotsee/fields.json | 8 +- protocols/spotsee/spotsee.ot2.apiv2.py | 56 +- 6 files changed, 512 insertions(+), 496 deletions(-) diff --git a/data/data/fields.csv b/data/data/fields.csv index ff09040cd..3adf671c7 100644 --- a/data/data/fields.csv +++ b/data/data/fields.csv @@ -890,6 +890,7 @@ num_col,10 num_cols,3 num_columns,1 num_curves,1 +num_cycles,1 num_daughter_plates,1 num_drug_plates,1 num_extracts,1 diff --git a/protoBuilds/3e0bef/3e0b3f.ot2.apiv2.py.json b/protoBuilds/3e0bef/3e0b3f.ot2.apiv2.py.json index 128e0c687..9758a2deb 100644 --- a/protoBuilds/3e0bef/3e0b3f.ot2.apiv2.py.json +++ b/protoBuilds/3e0bef/3e0b3f.ot2.apiv2.py.json @@ -1,5 +1,5 @@ { - "content": "from opentrons import protocol_api\nfrom opentrons import types\nfrom opentrons.protocol_api.labware import Well\nimport math\nfrom types import MethodType\nimport subprocess\nfrom opentrons.protocols.api_support.types import APIVersion\n\nmetadata = {\n 'protocolName': 'Omega Bio-Tek Mag-Bind Plant DNA DS Kit',\n 'author': 'Opentrons ',\n 'source': 'Custom Protocol Request',\n 'apiLevel': '2.13'\n}\n\nAUDIO_FILE_PATH = '/etc/audio/speaker-test.mp3'\n\n\ndef run_quiet_process(command):\n subprocess.check_output('{} &> /dev/null'.format(command), shell=True)\n\n\ndef test_speaker(song=AUDIO_FILE_PATH):\n print('Speaker')\n print('Next\t--> CTRL-C')\n try:\n run_quiet_process('mpg123 {}'.format(song))\n except KeyboardInterrupt:\n pass\n print()\n\n\ndef run(ctx: protocol_api.ProtocolContext):\n [\n _m300_mount,\n _num_samps,\n _inc_time,\n _mag_time,\n _air_dry,\n _add_water,\n _samp_labware,\n _asp_ht,\n _fin_wash,\n _elution_vol,\n _off_deck,\n _music\n ] = get_values( # noqa: F821 (<--- DO NOT REMOVE!)\n '_m300_mount',\n '_num_samps',\n '_inc_time',\n '_mag_time',\n '_air_dry',\n '_add_water',\n '_samp_labware',\n '_asp_ht',\n '_fin_wash',\n '_elution_vol',\n '_off_deck',\n '_music')\n\n if not 1 <= _num_samps <= 96:\n raise Exception('The Number of Samples should be between 1 and 96')\n\n # define all custom variables above here with descriptions\n\n m300_mount = _m300_mount # mount for 8-channel p300 pipette\n num_cols = math.ceil(_num_samps/8) # number of sample columns\n samp_labware = _samp_labware # labware containing sample\n elution_vol = _elution_vol # volume of elution buffer\n inc_time = _inc_time # time for binding step\n mag_time = _mag_time # time on magnetic module\n air_dry = _air_dry # time for air drying\n fin_wash = _fin_wash # volume for final wash removal\n asp_ht = _asp_ht # height from the top of the labware to aspirate from\n add_water = _add_water # True/False for adding water prior to air dry\n off_deck = _off_deck # True/False perform resuspension off-deck\n music = _music # play custom music (only in Oregon)\n\n mag_deck = ctx.load_module('magnetic module gen2', 7)\n\n # load labware\n rsvr_12 = [ctx.load_labware('nest_12_reservoir_15ml', s) for s in [2, 3]]\n rsvr_1 = ctx.load_labware('nest_1_reservoir_195ml', 10)\n pcr_plate = ctx.load_labware('nest_96_wellplate_100ul_pcr_full_skirt', 1)\n samp_plate = ctx.load_labware(samp_labware, 4)\n mag_plate = mag_deck.load_labware('nest_96_wellplate_2ml_deep')\n\n # load tipracks\n tips = [\n ctx.load_labware(\n 'opentrons_96_filtertiprack_200ul', s) for s in [5, 6, 8, 9, 11]\n ]\n all_tips = [t for rack in tips for t in rack.rows()[0]]\n t_start = 0\n t_end = int(num_cols)\n\n # load instrument\n m300 = ctx.load_instrument('p300_multi_gen2', m300_mount, tip_racks=tips)\n\n # extend well objects for improved liquid handling\n class WellH(Well):\n def __init__(self, well, min_height=0.5, comp_coeff=1.1,\n current_volume=0):\n super().__init__(well.parent, well._core, APIVersion(2, 13))\n self.well = well\n # specified minimum well bottom clearance\n self.min_height = min_height\n self.comp_coeff = comp_coeff\n # specified starting volume in ul\n self.current_volume = current_volume\n # cross sectional area\n if self.diameter is not None:\n self.radius = self.diameter/2\n cse = math.pi*(self.radius**2)\n elif self.length is not None:\n cse = self.length*self.width\n else:\n cse = None\n self.cse = cse\n # initial liquid level in mm from start vol\n if cse:\n self.height = (current_volume/cse)\n else:\n raise Exception('Labware definition must \\nsupply well radius or well length and width.')\n if self.height < min_height:\n self.height = min_height\n elif self.height > well.parent.highest_z:\n raise Exception('Specified liquid volume \\ncan not exceed the height of the labware.')\n\n def height_dec(self, vol, ppt, bottom=False):\n # decrement height (mm)\n dh = (vol/self.cse)*self.comp_coeff\n # tip immersion (mm) as fraction of tip length\n mm_immersed = 0.05*ppt._tip_racks[0].wells()[0].depth\n # decrement til target reaches specified min clearance\n self.height = self.height - dh if (\n (self.height - dh - mm_immersed) > self.min_height\n ) else self.min_height + mm_immersed\n self.current_volume = self.current_volume - vol if (\n self.current_volume - vol > 0) else 0\n tip_ht = self.height - mm_immersed if bottom is False else bottom\n return self.well.bottom(tip_ht)\n\n def height_inc(self, vol, top=False):\n # increment height (mm)\n ih = (vol/self.cse)*self.comp_coeff\n # keep calculated liquid ht between min clearance and well depth\n self.height = self.min_height if (\n self.height < self.min_height) else self.height\n self.height = (self.height + ih) if (\n (self.height + ih) < self.depth) else self.depth\n # increment\n self.current_volume += vol\n if top is False:\n tip_ht = self.height\n return self.well.bottom(tip_ht)\n else:\n return self.well.top()\n\n # pipette functions # INCLUDE ANY BINDING TO CLASS\n def aspirate_h(self, vol, source, rate=1, bottom=False):\n self.aspirate(\n vol, source.height_dec(vol, self, bottom=bottom), rate=rate)\n\n def dispense_h(self, vol, dest, rate=1, top=False):\n self.dispense(vol, dest.height_inc(vol, top=top), rate=rate)\n\n def slow_tip_withdrawal(\n self, speed_limit, well_location, to_surface=False):\n if self.mount == 'right':\n axis = 'A'\n else:\n axis = 'Z'\n previous_limit = None\n if axis in ctx.max_speeds.keys():\n for key, value in ctx.max_speeds.items():\n if key == axis:\n previous_limit = value\n ctx.max_speeds[axis] = speed_limit\n if to_surface is False:\n self.move_to(well_location.top())\n else:\n if isinstance(well_location, WellH):\n self.move_to(well_location.bottom().move(types.Point(\n x=0, y=0, z=well_location.height+(\n 20*(self._tip_racks[0].wells()[0].depth / 88)))))\n else:\n self.move_to(well_location.center())\n ctx.max_speeds[axis] = previous_limit\n\n def custom_pick_up(self, loc=None):\n nonlocal t_start\n nonlocal t_end\n # `custom_pick_up` will pause the protocol when all tip boxes are out\n # of tips, prompting the user to replace all tip racks. Once tipracks\n # reset, the protocol will start picking up tips from the first tip\n # box as defined in the slot order when assigning the labware def\n # for that tip box. `pick_up()` will track tips for both pipettes if\n # applicable.\n\n # :param loc: User can manually specify location for tip pick up\n\n if loc:\n self.pick_up_tip(loc)\n else:\n try:\n self.pick_up_tip()\n except protocol_api.labware.OutOfTipsError:\n flash_lights()\n ctx.pause('Replace empty tip racks')\n self.reset_tipracks()\n t_start = 0\n t_end = int(num_cols)\n ctx.set_rail_lights(True)\n self.pick_up_tip()\n\n # bind additional methods to pipettes\n for met in [aspirate_h, dispense_h, slow_tip_withdrawal, custom_pick_up]:\n setattr(\n m300, met.__name__,\n MethodType(met, m300))\n\n # reagents\n liquid_waste = rsvr_1.wells()[0].top()\n # cspl = [WellH(well) for well in rsvr_12[0].wells()[:6]]\n # for idx in range(num_cols):\n # cspl[idx//2].height_inc(720*8*1.1)\n\n # helper functions\n def flash_lights():\n for _ in range(19):\n ctx.set_rail_lights(not ctx.rail_lights_on)\n ctx.delay(seconds=0.25)\n\n def flow_rate(asp=92.86, disp=92.86, blow=92.86):\n # This function can be used to quickly modify the flow rates of the\n # m300. If no parameters are entered, the flow rates will be\n # reset.\n\n # :param asp: Aspiration flow rate, in uL/sec\n # :param disp: Dispense flow rate, in uL/sec\n\n m300.flow_rate.aspirate = asp\n m300.flow_rate.dispense = disp\n m300.flow_rate.blow_out = blow\n\n def remove_supernatant(vol, src):\n w = int(str(src).split(' ')[0][1:])\n if src.width is not None:\n radi = float(src.width)/4\n else:\n radi = float(src.diameter)/4\n x0 = radi if w % 2 == 0 else -radi\n while vol > 180:\n m300.aspirate(180, src.bottom().move(types.Point(x=x0, y=0, z=1)))\n m300.dispense(200, liquid_waste)\n m300.blow_out()\n m300.aspirate(20, liquid_waste)\n vol -= 180\n m300.aspirate(vol, src.bottom().move(types.Point(x=x0, y=0, z=0.7)))\n m300.dispense(vol+20, liquid_waste)\n m300.blow_out()\n m300.aspirate(10, liquid_waste)\n\n def wash(srcs, msg, sup=600):\n nonlocal t_start\n nonlocal t_end\n\n if mag_deck.status == 'engaged':\n mag_deck.disengage()\n ctx.comment(f'Performing wash step: {msg}')\n flow_rate()\n for idx, (col, src) in enumerate(zip(mag_samps_h, srcs)):\n m300.custom_pick_up()\n # src = srcs[idx//3]\n for i in range(2):\n flow_rate(asp=150, disp=150)\n if i == 1:\n m300.dispense(20, src.top(-1))\n m300.dispense(20, src)\n m300.mix(1, 200, src)\n flow_rate()\n m300.aspirate(200, src)\n m300.slow_tip_withdrawal(10, src, to_surface=True)\n m300.dispense(180, col.top(-2))\n flow_rate(asp=10)\n m300.aspirate(20, col.top())\n m300.dispense(20, src.top())\n flow_rate()\n m300.mix(1, 140, src)\n m300.aspirate(140, src)\n m300.slow_tip_withdrawal(10, src, to_surface=True)\n m300.dispense(140, col)\n if not off_deck:\n m300.mix(10, 100, col)\n ctx.delay(seconds=5)\n m300.slow_tip_withdrawal(10, col, to_surface=True)\n m300.blow_out()\n m300.touch_tip(speed=40)\n m300.aspirate(10, col.top())\n m300.drop_tip()\n\n if off_deck:\n flash_lights()\n ctx.pause('Please remove plate for manual resuspension')\n\n mag_deck.engage()\n mag_msg = f'Incubating on Mag Deck for {mag_time} minutes'\n ctx.delay(minutes=mag_time, msg=mag_msg)\n\n # Discard Supernatant\n ctx.comment(f'Removing supernatant for wash: {msg}')\n t_start += num_cols\n t_end += num_cols\n for src, t_d in zip(mag_samps_h, all_tips[t_start:t_end]):\n m300.custom_pick_up()\n remove_supernatant(sup, src)\n m300.drop_tip(t_d)\n\n # plate, tube rack maps\n init_samps = samp_plate.rows()[0][:num_cols]\n mag_samps = mag_plate.rows()[0][:num_cols]\n mag_samps_h = [WellH(well) for well in mag_samps]\n pcr_samps = pcr_plate.rows()[0][:num_cols]\n\n # protocol\n ctx.set_rail_lights(True)\n # # Transfer 700µL CSPL Buffer + 20µL Prot K\n # ctx.comment('Transferring 720uL of CSPL Buffer + Proteinase K')\n #\n # m300.custom_pick_up()\n # for idx, col in enumerate(init_samps):\n # src = cspl[idx//2]\n # for _ in range(4):\n # m300.aspirate_h(180, src)\n # m300.slow_tip_withdrawal(10, src, to_surface=True)\n # m300.dispense(180, col.top(-2))\n # m300.drop_tip()\n #\n # flash_lights()\n # ctx.pause('Please remove samples and incubate at 56C for 30 minutes, \\n # then centrifuge at 4000g for 10 minutes. Once complete, please replace \\n # samples on the deck and place ensure 12-well reservoirs are filled with \\n # necessary reagents in deck slots 2 and 3. When ready, click RESUME.')\n # ctx.set_rail_lights(True)\n # Creating reagent variables for second part of protocol\n rbb = [WellH(well, current_volume=0) for well in rsvr_12[0].wells()[:4]]\n cspw1 = [WellH(well) for well in rsvr_12[0].wells()[4:8]]\n cspw2 = [WellH(well) for well in rsvr_12[0].wells()[8:]]\n spm = [WellH(well) for well in rsvr_12[1].wells()[:8]]\n h2o = rsvr_12[1]['A9']\n elution_buffer = [WellH(well) for well in rsvr_12[1].wells()[10:]]\n\n cspw1_wells = []\n cspw2_wells = []\n spm1_wells = []\n spm2_wells = []\n elution_wells = []\n\n for idx in range(num_cols):\n rbb[idx//3].height_inc(525*8)\n cspw1[idx//3].height_inc(500*8)\n cspw2[idx//3].height_inc(500*8)\n elution_buffer[idx//6].height_inc(elution_vol*8)\n cspw1_wells.append(cspw1[idx//3])\n cspw2_wells.append(cspw2[idx//3])\n elution_wells.append(elution_buffer[idx//6])\n\n for idx in range(num_cols*2):\n spm[idx//3].height_inc(500*8)\n if idx % 2 == 0:\n spm1_wells.append(spm[idx//3])\n else:\n spm2_wells.append(spm[idx//3])\n\n reagent_keys = {\n 'RBB + RNase + Mag-Bind Beads': rbb,\n 'CSPW 1 Buffer': cspw1,\n 'CSPW 2 Buffer': cspw2,\n 'Elution Buffer': elution_buffer,\n 'SPM': spm,\n }\n for key in reagent_keys:\n for x in reagent_keys[key]:\n ctx.comment(f'load {x.current_volume} of {key} in {x}')\n # for x in rbb:\n # ctx.comment(f'load {x.current_volume} in {x}')\n\n if samp_labware == 'qiagen_96_tuberack_1200ul':\n ctx.comment('Transferring 500uL of sample to plate on MagDeck')\n\n flow_rate(asp=20)\n for src, dest in zip(init_samps, mag_samps_h):\n src_asp = src.top(-asp_ht)\n m300.custom_pick_up()\n for i in range(2):\n m300.aspirate(20, src.top())\n m300.aspirate(180, src_asp)\n m300.slow_tip_withdrawal(10, src)\n m300.dispense_h(180, dest)\n m300.slow_tip_withdrawal(10, dest, to_surface=True)\n m300.dispense(20, dest.bottom(5))\n m300.aspirate(20, src.top())\n m300.aspirate(140, src_asp)\n m300.dispense_h(140, dest)\n m300.slow_tip_withdrawal(10, dest, to_surface=True)\n m300.dispense(20, dest.bottom(5))\n m300.drop_tip()\n flow_rate()\n else:\n flash_lights()\n ctx.pause('Please make sure samples are loaded on MagDeck')\n\n # Transfer 5uL RNAse + 500uL RBB buffer + 20uL Mag-Bind Beads\n ctx.comment('Transferring 5uL RNAse + 500uL RBB buffer + \\n20uL Mag-Bind Beads')\n\n m300.custom_pick_up()\n flow_rate(blow=10)\n for idx, dest in enumerate(mag_samps):\n src = rbb[idx//3]\n for _ in range(3):\n flow_rate(asp=20, disp=20)\n m300.mix(1, 100, src)\n m300.aspirate(175, src)\n m300.slow_tip_withdrawal(10, src, to_surface=True)\n flow_rate(disp=10)\n m300.dispense(75, dest.top(-1))\n flow_rate(disp=5)\n m300.dispense(50, dest.top(-1))\n ctx.delay(seconds=3)\n m300.dispense(30, dest.top(-1))\n ctx.delay(seconds=3)\n flow_rate(disp=2)\n m300.dispense(20, dest.top(-1))\n m300.blow_out()\n m300.touch_tip(speed=40)\n m300.drop_tip()\n flow_rate()\n\n if off_deck:\n flash_lights()\n ctx.pause('Please incubate samples for 10 minutes post resuspension.')\n else:\n incubate_msg = f'Incubating at room temperature for {inc_time} \\nminutes plus mixing'\n ctx.comment(incubate_msg)\n\n if num_cols > 1:\n num_mixes = math.ceil(2*inc_time/num_cols)\n else:\n num_mixes = 10\n for _ in range(num_mixes):\n for col, t_d in zip(mag_samps, all_tips[t_start:t_end]):\n if _ == 0:\n m300.custom_pick_up()\n if not m300.has_tip:\n m300.custom_pick_up(t_d)\n m300.mix(8, 150, col)\n ctx.delay(seconds=1)\n m300.blow_out(col.top(-2))\n m300.touch_tip()\n if len(mag_samps) > 1:\n m300.drop_tip(t_d)\n else:\n ctx.delay(seconds=30)\n if m300.has_tip:\n m300.drop_tip(t_d)\n\n t_start += num_cols\n t_end += num_cols\n mag_deck.engage()\n mag_msg = f'Incubating on Mag Deck for {mag_time} minutes'\n ctx.delay(minutes=mag_time, msg=mag_msg)\n\n # Discard Supernatant\n ctx.comment('Removing supernatant')\n flow_rate(asp=40, disp=40)\n for src, t_d in zip(mag_samps_h, all_tips[t_start:t_end]):\n m300.custom_pick_up()\n remove_supernatant(540, src)\n m300.drop_tip()\n m300.custom_pick_up()\n remove_supernatant(540, src)\n m300.drop_tip(t_d)\n\n flow_rate()\n\n # Wash with 500uL CSPW1 Buffer\n wash(cspw1_wells, 'CSPW1')\n\n # Wash with 500uL CSPW2 Buffer\n wash(cspw2_wells, 'CSPW2')\n\n # Wash with SPM Buffer (1)\n wash(spm1_wells, 'SPM (first wash)')\n\n # Wash with SPM Buffer (2)\n wash(spm2_wells, 'SPM (second wash)', fin_wash)\n\n # Air dry for 10 minutes\n mag_deck.engage()\n if add_water:\n for src in mag_samps_h:\n m300.custom_pick_up()\n m300.aspirate(100, h2o)\n flow_rate(asp=30, disp=30)\n m300.dispense(80, src)\n ctx.delay(seconds=0.5)\n m300.aspirate(100, src)\n flow_rate()\n m300.dispense(120, liquid_waste)\n m300.blow_out()\n m300.drop_tip()\n\n ctx.home()\n air_dry_msg = f'Air drying the beads for {air_dry} minutes. \\nPlease add elution buffer at 65C to 12-well reservoir.'\n ctx.delay(minutes=air_dry, msg=air_dry_msg)\n flash_lights()\n if not ctx.is_simulating():\n if music:\n test_speaker('/var/lib/jupyter/notebooks/mr-blue-sky-ot2.mp3')\n else:\n test_speaker()\n ctx.pause('Please check the Well Plate')\n\n mag_deck.disengage()\n # Add Elution Buffer\n ctx.comment(f'Adding {elution_vol}uL Elution Buffer to samples')\n\n for idx, (col, src) in enumerate(zip(mag_samps_h, elution_wells)):\n m300.custom_pick_up()\n m300.aspirate(elution_vol, src)\n m300.slow_tip_withdrawal(10, src, to_surface=True)\n m300.dispense_h(elution_vol, col)\n if not off_deck:\n m300.mix(10, 50, col)\n m300.slow_tip_withdrawal(10, col, to_surface=True)\n m300.blow_out(col.bottom(6))\n for _ in range(2):\n m300.move_to(col.bottom(5))\n m300.move_to(col.bottom(6))\n m300.drop_tip()\n\n flash_lights()\n ctx.home()\n if not ctx.is_simulating():\n if music:\n test_speaker('/var/lib/jupyter/notebooks/all-i-want-ot2.mp3')\n else:\n test_speaker()\n ctx.pause('Please remove samples and incubate at 65C for 5 minutes.\\nWhen complete, replace samples and click RESUME')\n ctx.set_rail_lights(True)\n\n # Transfer elution to PCR plate\n if not off_deck:\n for col in mag_samps_h:\n m300.custom_pick_up()\n m300.mix(10, 50, col)\n m300.slow_tip_withdrawal(10, col, to_surface=True)\n m300.blow_out(col.bottom(6))\n for _ in range(2):\n m300.move_to(col.bottom(5))\n m300.move_to(col.bottom(6))\n m300.drop_tip(home_after=False)\n else:\n ctx.comment('Please perform manual resupsension off deck.')\n\n mag_deck.engage()\n mag_msg = 'Incubating on Mag Deck for 3 minutes'\n ctx.delay(minutes=3, msg=mag_msg)\n\n ctx.comment(f'Transferring {elution_vol}uL to final PCR plate')\n t_start += num_cols\n if t_start >= 60:\n t_start -= 60\n\n flow_rate(asp=20)\n for src, dest, tip in zip(mag_samps, pcr_samps, all_tips[t_start:]):\n w = int(str(src).split(' ')[0][1:])\n if src.width is not None:\n radi = float(src.width)/4\n else:\n radi = float(src.diameter)/4\n x0 = radi if w % 2 == 0 else -radi\n m300.custom_pick_up()\n m300.aspirate(\n elution_vol, src.bottom().move(types.Point(x=x0, y=0, z=1)))\n m300.dispense(elution_vol, dest)\n m300.drop_tip(tip)\n\n mag_deck.disengage()\n ctx.comment('Protocol complete! Please store samples at -20C or \\ncontinue processing')\n", + "content": "# flake8: noqa\nfrom opentrons import protocol_api\nfrom opentrons import types\nfrom opentrons.protocol_api.labware import Well\nimport math\nfrom types import MethodType\nimport subprocess\nfrom opentrons.protocols.api_support.types import APIVersion\n\nmetadata = {\n 'protocolName': 'Omega Bio-Tek Mag-Bind Plant DNA DS Kit',\n 'author': 'Opentrons ',\n 'source': 'Custom Protocol Request',\n 'apiLevel': '2.13'\n}\n\nAUDIO_FILE_PATH = '/etc/audio/speaker-test.mp3'\n\n\ndef run_quiet_process(command):\n subprocess.check_output('{} &> /dev/null'.format(command), shell=True)\n\n\ndef test_speaker(song=AUDIO_FILE_PATH):\n print('Speaker')\n print('Next\\t--> CTRL-C')\n try:\n run_quiet_process('mpg123 {}'.format(song))\n except KeyboardInterrupt:\n pass\n print()\n\n\ndef run(ctx: protocol_api.ProtocolContext):\n [\n _m300_mount,\n _num_samps,\n _inc_time,\n _mag_time,\n _air_dry,\n _add_water,\n _samp_labware,\n _asp_ht,\n _fin_wash,\n _elution_vol,\n _off_deck,\n _music\n ] = get_values( # noqa: F821 (<--- DO NOT REMOVE!)\n '_m300_mount',\n '_num_samps',\n '_inc_time',\n '_mag_time',\n '_air_dry',\n '_add_water',\n '_samp_labware',\n '_asp_ht',\n '_fin_wash',\n '_elution_vol',\n '_off_deck',\n '_music')\n\n if not 1 <= _num_samps <= 96:\n raise Exception('The Number of Samples should be between 1 and 96')\n\n # define all custom variables above here with descriptions\n\n m300_mount = _m300_mount # mount for 8-channel p300 pipette\n num_cols = math.ceil(_num_samps/8) # number of sample columns\n samp_labware = _samp_labware # labware containing sample\n elution_vol = _elution_vol # volume of elution buffer\n inc_time = _inc_time # time for binding step\n mag_time = _mag_time # time on magnetic module\n air_dry = _air_dry # time for air drying\n fin_wash = _fin_wash # volume for final wash removal\n asp_ht = _asp_ht # height from the top of the labware to aspirate from\n add_water = _add_water # True/False for adding water prior to air dry\n off_deck = _off_deck # True/False perform resuspension off-deck\n music = _music # play custom music (only in Oregon)\n\n mag_deck = ctx.load_module('magnetic module gen2', 7)\n\n # load labware\n rsvr_12 = [ctx.load_labware('nest_12_reservoir_15ml', s) for s in [2, 3]]\n rsvr_1 = ctx.load_labware('nest_1_reservoir_195ml', 10)\n pcr_plate = ctx.load_labware('nest_96_wellplate_100ul_pcr_full_skirt', 1)\n samp_plate = ctx.load_labware(samp_labware, 4)\n mag_plate = mag_deck.load_labware('nest_96_wellplate_2ml_deep')\n\n # load tipracks\n tips = [\n ctx.load_labware(\n 'opentrons_96_filtertiprack_200ul', s) for s in [5, 6, 8, 9, 11]\n ]\n all_tips = [t for rack in tips for t in rack.rows()[0]]\n t_start = 0\n t_end = int(num_cols)\n\n # load instrument\n m300 = ctx.load_instrument('p300_multi_gen2', m300_mount, tip_racks=tips)\n\n if not ctx.is_simulating:\n\n # extend well objects for improved liquid handling\n class WellH(Well):\n def __init__(self, well, min_height=0.5, comp_coeff=1.1,\n current_volume=0):\n super().__init__(well.parent, well._core, APIVersion(2, 13))\n self.well = well\n # specified minimum well bottom clearance\n self.min_height = min_height\n self.comp_coeff = comp_coeff\n # specified starting volume in ul\n self.current_volume = current_volume\n # cross sectional area\n if self.diameter is not None:\n self.radius = self.diameter/2\n cse = math.pi*(self.radius**2)\n elif self.length is not None:\n cse = self.length*self.width\n else:\n cse = None\n self.cse = cse\n # initial liquid level in mm from start vol\n if cse:\n self.height = (current_volume/cse)\n else:\n raise Exception('Labware definition must \\\n supply well radius or well length and width.')\n if self.height < min_height:\n self.height = min_height\n elif self.height > well.parent.highest_z:\n raise Exception('Specified liquid volume \\\n can not exceed the height of the labware.')\n\n def height_dec(self, vol, ppt, bottom=False):\n # decrement height (mm)\n dh = (vol/self.cse)*self.comp_coeff\n # tip immersion (mm) as fraction of tip length\n mm_immersed = 0.05*ppt._tip_racks[0].wells()[0].depth\n # decrement til target reaches specified min clearance\n self.height = self.height - dh if (\n (self.height - dh - mm_immersed) > self.min_height\n ) else self.min_height + mm_immersed\n self.current_volume = self.current_volume - vol if (\n self.current_volume - vol > 0) else 0\n tip_ht = self.height - mm_immersed if bottom is False else bottom\n return self.well.bottom(tip_ht)\n\n def height_inc(self, vol, top=False):\n # increment height (mm)\n ih = (vol/self.cse)*self.comp_coeff\n # keep calculated liquid ht between min clearance and well depth\n self.height = self.min_height if (\n self.height < self.min_height) else self.height\n self.height = (self.height + ih) if (\n (self.height + ih) < self.depth) else self.depth\n # increment\n self.current_volume += vol\n if top is False:\n tip_ht = self.height\n return self.well.bottom(tip_ht)\n else:\n return self.well.top()\n\n # pipette functions # INCLUDE ANY BINDING TO CLASS\n def aspirate_h(self, vol, source, rate=1, bottom=False):\n self.aspirate(\n vol, source.height_dec(vol, self, bottom=bottom), rate=rate)\n\n def dispense_h(self, vol, dest, rate=1, top=False):\n self.dispense(vol, dest.height_inc(vol, top=top), rate=rate)\n\n def slow_tip_withdrawal(\n self, speed_limit, well_location, to_surface=False):\n if self.mount == 'right':\n axis = 'A'\n else:\n axis = 'Z'\n previous_limit = None\n if axis in ctx.max_speeds.keys():\n for key, value in ctx.max_speeds.items():\n if key == axis:\n previous_limit = value\n ctx.max_speeds[axis] = speed_limit\n if to_surface is False:\n self.move_to(well_location.top())\n else:\n if isinstance(well_location, WellH):\n self.move_to(well_location.bottom().move(types.Point(\n x=0, y=0, z=well_location.height+(\n 20*(self._tip_racks[0].wells()[0].depth / 88)))))\n else:\n self.move_to(well_location.center())\n ctx.max_speeds[axis] = previous_limit\n\n def custom_pick_up(self, loc=None):\n nonlocal t_start\n nonlocal t_end\n # `custom_pick_up` will pause the protocol when all tip boxes are out\n # of tips, prompting the user to replace all tip racks. Once tipracks\n # reset, the protocol will start picking up tips from the first tip\n # box as defined in the slot order when assigning the labware def\n # for that tip box. `pick_up()` will track tips for both pipettes if\n # applicable.\n\n # :param loc: User can manually specify location for tip pick up\n\n if loc:\n self.pick_up_tip(loc)\n else:\n try:\n self.pick_up_tip()\n except protocol_api.labware.OutOfTipsError:\n flash_lights()\n ctx.pause('Replace empty tip racks')\n self.reset_tipracks()\n t_start = 0\n t_end = int(num_cols)\n ctx.set_rail_lights(True)\n self.pick_up_tip()\n\n # bind additional methods to pipettes\n for met in [aspirate_h, dispense_h, slow_tip_withdrawal, custom_pick_up]:\n setattr(\n m300, met.__name__,\n MethodType(met, m300))\n\n # reagents\n liquid_waste = rsvr_1.wells()[0].top()\n # cspl = [WellH(well) for well in rsvr_12[0].wells()[:6]]\n # for idx in range(num_cols):\n # cspl[idx//2].height_inc(720*8*1.1)\n\n # helper functions\n def flash_lights():\n for _ in range(19):\n ctx.set_rail_lights(not ctx.rail_lights_on)\n ctx.delay(seconds=0.25)\n\n def flow_rate(asp=92.86, disp=92.86, blow=92.86):\n # This function can be used to quickly modify the flow rates of the\n # m300. If no parameters are entered, the flow rates will be\n # reset.\n\n # :param asp: Aspiration flow rate, in uL/sec\n # :param disp: Dispense flow rate, in uL/sec\n\n m300.flow_rate.aspirate = asp\n m300.flow_rate.dispense = disp\n m300.flow_rate.blow_out = blow\n\n def remove_supernatant(vol, src):\n w = int(str(src).split(' ')[0][1:])\n if src.width is not None:\n radi = float(src.width)/4\n else:\n radi = float(src.diameter)/4\n x0 = radi if w % 2 == 0 else -radi\n while vol > 180:\n m300.aspirate(180, src.bottom().move(types.Point(x=x0, y=0, z=1)))\n m300.dispense(200, liquid_waste)\n m300.blow_out()\n m300.aspirate(20, liquid_waste)\n vol -= 180\n m300.aspirate(vol, src.bottom().move(types.Point(x=x0, y=0, z=0.7)))\n m300.dispense(vol+20, liquid_waste)\n m300.blow_out()\n m300.aspirate(10, liquid_waste)\n\n def wash(srcs, msg, sup=600):\n nonlocal t_start\n nonlocal t_end\n\n if mag_deck.status == 'engaged':\n mag_deck.disengage()\n ctx.comment(f'Performing wash step: {msg}')\n flow_rate()\n for idx, (col, src) in enumerate(zip(mag_samps_h, srcs)):\n m300.custom_pick_up()\n # src = srcs[idx//3]\n for i in range(2):\n flow_rate(asp=150, disp=150)\n if i == 1:\n m300.dispense(20, src.top(-1))\n m300.dispense(20, src)\n m300.mix(1, 200, src)\n flow_rate()\n m300.aspirate(200, src)\n m300.slow_tip_withdrawal(10, src, to_surface=True)\n m300.dispense(180, col.top(-2))\n flow_rate(asp=10)\n m300.aspirate(20, col.top())\n m300.dispense(20, src.top())\n flow_rate()\n m300.mix(1, 140, src)\n m300.aspirate(140, src)\n m300.slow_tip_withdrawal(10, src, to_surface=True)\n m300.dispense(140, col)\n if not off_deck:\n m300.mix(10, 100, col)\n ctx.delay(seconds=5)\n m300.slow_tip_withdrawal(10, col, to_surface=True)\n m300.blow_out()\n m300.touch_tip(speed=40)\n m300.aspirate(10, col.top())\n m300.drop_tip()\n\n if off_deck:\n flash_lights()\n ctx.pause('Please remove plate for manual resuspension')\n\n mag_deck.engage()\n mag_msg = f'Incubating on Mag Deck for {mag_time} minutes'\n ctx.delay(minutes=mag_time, msg=mag_msg)\n\n # Discard Supernatant\n ctx.comment(f'Removing supernatant for wash: {msg}')\n t_start += num_cols\n t_end += num_cols\n for src, t_d in zip(mag_samps_h, all_tips[t_start:t_end]):\n m300.custom_pick_up()\n remove_supernatant(sup, src)\n m300.drop_tip(t_d)\n\n # plate, tube rack maps\n init_samps = samp_plate.rows()[0][:num_cols]\n mag_samps = mag_plate.rows()[0][:num_cols]\n mag_samps_h = [WellH(well) for well in mag_samps]\n pcr_samps = pcr_plate.rows()[0][:num_cols]\n\n # protocol\n ctx.set_rail_lights(True)\n # # Transfer 700\u00b5L CSPL Buffer + 20\u00b5L Prot K\n # ctx.comment('Transferring 720uL of CSPL Buffer + Proteinase K')\n #\n # m300.custom_pick_up()\n # for idx, col in enumerate(init_samps):\n # src = cspl[idx//2]\n # for _ in range(4):\n # m300.aspirate_h(180, src)\n # m300.slow_tip_withdrawal(10, src, to_surface=True)\n # m300.dispense(180, col.top(-2))\n # m300.drop_tip()\n #\n # flash_lights()\n # ctx.pause('Please remove samples and incubate at 56C for 30 minutes, \\\n # then centrifuge at 4000g for 10 minutes. Once complete, please replace \\\n # samples on the deck and place ensure 12-well reservoirs are filled with \\\n # necessary reagents in deck slots 2 and 3. When ready, click RESUME.')\n # ctx.set_rail_lights(True)\n # Creating reagent variables for second part of protocol\n rbb = [WellH(well, current_volume=0) for well in rsvr_12[0].wells()[:4]]\n cspw1 = [WellH(well) for well in rsvr_12[0].wells()[4:8]]\n cspw2 = [WellH(well) for well in rsvr_12[0].wells()[8:]]\n spm = [WellH(well) for well in rsvr_12[1].wells()[:8]]\n h2o = rsvr_12[1]['A9']\n elution_buffer = [WellH(well) for well in rsvr_12[1].wells()[10:]]\n\n cspw1_wells = []\n cspw2_wells = []\n spm1_wells = []\n spm2_wells = []\n elution_wells = []\n\n for idx in range(num_cols):\n rbb[idx//3].height_inc(525*8)\n cspw1[idx//3].height_inc(500*8)\n cspw2[idx//3].height_inc(500*8)\n elution_buffer[idx//6].height_inc(elution_vol*8)\n cspw1_wells.append(cspw1[idx//3])\n cspw2_wells.append(cspw2[idx//3])\n elution_wells.append(elution_buffer[idx//6])\n\n for idx in range(num_cols*2):\n spm[idx//3].height_inc(500*8)\n if idx % 2 == 0:\n spm1_wells.append(spm[idx//3])\n else:\n spm2_wells.append(spm[idx//3])\n\n reagent_keys = {\n 'RBB + RNase + Mag-Bind Beads': rbb,\n 'CSPW 1 Buffer': cspw1,\n 'CSPW 2 Buffer': cspw2,\n 'Elution Buffer': elution_buffer,\n 'SPM': spm,\n }\n for key in reagent_keys:\n for x in reagent_keys[key]:\n ctx.comment(f'load {x.current_volume} of {key} in {x}')\n # for x in rbb:\n # ctx.comment(f'load {x.current_volume} in {x}')\n\n if samp_labware == 'qiagen_96_tuberack_1200ul':\n ctx.comment('Transferring 500uL of sample to plate on MagDeck')\n\n flow_rate(asp=20)\n for src, dest in zip(init_samps, mag_samps_h):\n src_asp = src.top(-asp_ht)\n m300.custom_pick_up()\n for i in range(2):\n m300.aspirate(20, src.top())\n m300.aspirate(180, src_asp)\n m300.slow_tip_withdrawal(10, src)\n m300.dispense_h(180, dest)\n m300.slow_tip_withdrawal(10, dest, to_surface=True)\n m300.dispense(20, dest.bottom(5))\n m300.aspirate(20, src.top())\n m300.aspirate(140, src_asp)\n m300.dispense_h(140, dest)\n m300.slow_tip_withdrawal(10, dest, to_surface=True)\n m300.dispense(20, dest.bottom(5))\n m300.drop_tip()\n flow_rate()\n else:\n flash_lights()\n ctx.pause('Please make sure samples are loaded on MagDeck')\n\n # Transfer 5uL RNAse + 500uL RBB buffer + 20uL Mag-Bind Beads\n ctx.comment('Transferring 5uL RNAse + 500uL RBB buffer + \\\n 20uL Mag-Bind Beads')\n\n m300.custom_pick_up()\n flow_rate(blow=10)\n for idx, dest in enumerate(mag_samps):\n src = rbb[idx//3]\n for _ in range(3):\n flow_rate(asp=20, disp=20)\n m300.mix(1, 100, src)\n m300.aspirate(175, src)\n m300.slow_tip_withdrawal(10, src, to_surface=True)\n flow_rate(disp=10)\n m300.dispense(75, dest.top(-1))\n flow_rate(disp=5)\n m300.dispense(50, dest.top(-1))\n ctx.delay(seconds=3)\n m300.dispense(30, dest.top(-1))\n ctx.delay(seconds=3)\n flow_rate(disp=2)\n m300.dispense(20, dest.top(-1))\n m300.blow_out()\n m300.touch_tip(speed=40)\n m300.drop_tip()\n flow_rate()\n\n if off_deck:\n flash_lights()\n ctx.pause('Please incubate samples for 10 minutes post resuspension.')\n else:\n incubate_msg = f'Incubating at room temperature for {inc_time} \\\n minutes plus mixing'\n ctx.comment(incubate_msg)\n\n if num_cols > 1:\n num_mixes = math.ceil(2*inc_time/num_cols)\n else:\n num_mixes = 10\n for _ in range(num_mixes):\n for col, t_d in zip(mag_samps, all_tips[t_start:t_end]):\n if _ == 0:\n m300.custom_pick_up()\n if not m300.has_tip:\n m300.custom_pick_up(t_d)\n m300.mix(8, 150, col)\n ctx.delay(seconds=1)\n m300.blow_out(col.top(-2))\n m300.touch_tip()\n if len(mag_samps) > 1:\n m300.drop_tip(t_d)\n else:\n ctx.delay(seconds=30)\n if m300.has_tip:\n m300.drop_tip(t_d)\n\n t_start += num_cols\n t_end += num_cols\n mag_deck.engage()\n mag_msg = f'Incubating on Mag Deck for {mag_time} minutes'\n ctx.delay(minutes=mag_time, msg=mag_msg)\n\n # Discard Supernatant\n ctx.comment('Removing supernatant')\n flow_rate(asp=40, disp=40)\n for src, t_d in zip(mag_samps_h, all_tips[t_start:t_end]):\n m300.custom_pick_up()\n remove_supernatant(540, src)\n m300.drop_tip()\n m300.custom_pick_up()\n remove_supernatant(540, src)\n m300.drop_tip(t_d)\n\n flow_rate()\n\n # Wash with 500uL CSPW1 Buffer\n wash(cspw1_wells, 'CSPW1')\n\n # Wash with 500uL CSPW2 Buffer\n wash(cspw2_wells, 'CSPW2')\n\n # Wash with SPM Buffer (1)\n wash(spm1_wells, 'SPM (first wash)')\n\n # Wash with SPM Buffer (2)\n wash(spm2_wells, 'SPM (second wash)', fin_wash)\n\n # Air dry for 10 minutes\n mag_deck.engage()\n if add_water:\n for src in mag_samps_h:\n m300.custom_pick_up()\n m300.aspirate(100, h2o)\n flow_rate(asp=30, disp=30)\n m300.dispense(80, src)\n ctx.delay(seconds=0.5)\n m300.aspirate(100, src)\n flow_rate()\n m300.dispense(120, liquid_waste)\n m300.blow_out()\n m300.drop_tip()\n\n ctx.home()\n air_dry_msg = f'Air drying the beads for {air_dry} minutes. \\\n Please add elution buffer at 65C to 12-well reservoir.'\n ctx.delay(minutes=air_dry, msg=air_dry_msg)\n flash_lights()\n if not ctx.is_simulating():\n if music:\n test_speaker('/var/lib/jupyter/notebooks/mr-blue-sky-ot2.mp3')\n else:\n test_speaker()\n ctx.pause('Please check the Well Plate')\n\n mag_deck.disengage()\n # Add Elution Buffer\n ctx.comment(f'Adding {elution_vol}uL Elution Buffer to samples')\n\n for idx, (col, src) in enumerate(zip(mag_samps_h, elution_wells)):\n m300.custom_pick_up()\n m300.aspirate(elution_vol, src)\n m300.slow_tip_withdrawal(10, src, to_surface=True)\n m300.dispense_h(elution_vol, col)\n if not off_deck:\n m300.mix(10, 50, col)\n m300.slow_tip_withdrawal(10, col, to_surface=True)\n m300.blow_out(col.bottom(6))\n for _ in range(2):\n m300.move_to(col.bottom(5))\n m300.move_to(col.bottom(6))\n m300.drop_tip()\n\n flash_lights()\n ctx.home()\n if not ctx.is_simulating():\n if music:\n test_speaker('/var/lib/jupyter/notebooks/all-i-want-ot2.mp3')\n else:\n test_speaker()\n ctx.pause('Please remove samples and incubate at 65C for 5 minutes.\\\n When complete, replace samples and click RESUME')\n ctx.set_rail_lights(True)\n\n # Transfer elution to PCR plate\n if not off_deck:\n for col in mag_samps_h:\n m300.custom_pick_up()\n m300.mix(10, 50, col)\n m300.slow_tip_withdrawal(10, col, to_surface=True)\n m300.blow_out(col.bottom(6))\n for _ in range(2):\n m300.move_to(col.bottom(5))\n m300.move_to(col.bottom(6))\n m300.drop_tip(home_after=False)\n else:\n ctx.comment('Please perform manual resupsension off deck.')\n\n mag_deck.engage()\n mag_msg = 'Incubating on Mag Deck for 3 minutes'\n ctx.delay(minutes=3, msg=mag_msg)\n\n ctx.comment(f'Transferring {elution_vol}uL to final PCR plate')\n t_start += num_cols\n if t_start >= 60:\n t_start -= 60\n\n flow_rate(asp=20)\n for src, dest, tip in zip(mag_samps, pcr_samps, all_tips[t_start:]):\n w = int(str(src).split(' ')[0][1:])\n if src.width is not None:\n radi = float(src.width)/4\n else:\n radi = float(src.diameter)/4\n x0 = radi if w % 2 == 0 else -radi\n m300.custom_pick_up()\n m300.aspirate(\n elution_vol, src.bottom().move(types.Point(x=x0, y=0, z=1)))\n m300.dispense(elution_vol, dest)\n m300.drop_tip(tip)\n\n mag_deck.disengage()\n ctx.comment('Protocol complete! Please store samples at -20C or \\\n continue processing')\n", "custom_labware_defs": [ { "brand": { @@ -1329,7 +1329,7 @@ } ], "metadata": { - "apiLevel": "2.11", + "apiLevel": "2.13", "author": "Opentrons ", "protocolName": "Omega Bio-Tek Mag-Bind Plant DNA DS Kit", "source": "Custom Protocol Request" diff --git a/protoBuilds/spotsee/spotsee.ot2.apiv2.py.json b/protoBuilds/spotsee/spotsee.ot2.apiv2.py.json index f0da6c924..4f0bc1dcf 100644 --- a/protoBuilds/spotsee/spotsee.ot2.apiv2.py.json +++ b/protoBuilds/spotsee/spotsee.ot2.apiv2.py.json @@ -1,5 +1,5 @@ { - "content": "metadata = {\n 'protocolName': 'Spotsee Well Distribution Protocol',\n 'author': 'Rami Farawi ',\n 'source': 'Custom Protocol Request',\n 'apiLevel': '2.13'\n}\n\n\ndef run(ctx):\n\n [vol, labware, start_tip,\n p20_mount, p300_mount] = get_values( # noqa: F821\n \"vol\", \"labware\", \"start_tip\", \"p20_mount\", \"p300_mount\")\n\n # p20_mount = 'left'\n # vol = 25\n # p300_mount = 'right'\n # labware = '40_well_plate'\n # start_tip = 4\n\n # labware\n reservoir = ctx.load_labware('nest_1_reservoir_195ml', 10)\n plate = ctx.load_labware(labware, 1)\n tips = [ctx.load_labware('opentrons_96_filtertiprack_20ul' if vol < 20 else 'opentrons_96_tiprack_300ul', slot) # noqa: E501\n for slot in [11]]\n\n red_wells = [\n 'J1', 'I1', 'H1', 'G1', 'F1', 'E1', 'D1', 'C1', 'B1', 'A1',\n 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 'J2',\n 'J3', 'I3', 'H3', 'G3', 'F3', 'E3', 'D3', 'C3', 'B3', 'A3',\n 'A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4', 'I4', 'J4',\n 'J5', 'I5', 'H5', 'G5', 'F5', 'E5', 'D5', 'C5', 'B5', 'A5',\n 'A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6', 'I6', 'J6',\n 'J7', 'I7', 'H7', 'G7', 'F7', 'E7', 'D7', 'C7', 'B7', 'A7',\n 'A8', 'B8', 'C8', 'D8', 'E8', 'F8', 'G8', 'H8', 'I8', 'J8'\n ]\n\n # pipettes\n p20 = ctx.load_instrument('p20_single_gen2', p20_mount, tip_racks=tips)\n p300 = ctx.load_instrument('p300_single_gen2', p300_mount, tip_racks=tips)\n pip = p20 if vol < 20 else p300\n\n # mapping\n buffer = reservoir.wells()[0]\n\n # protocol\n ctx.comment('\\n---------------ADDING BUFFER TO PLATE----------------\\n\\n')\n if labware == '80_well_plate':\n num_asp = pip.max_volume // vol\n chunks = [red_wells[i:i+num_asp] for i in range(0, len(red_wells),\n num_asp)]\n\n pip.pick_up_tip(tips[0].wells()[start_tip-1])\n for chunk in chunks:\n pip.aspirate(num_asp*vol, buffer)\n for well in chunk:\n pip.dispense(vol, plate.wells_by_name()[well])\n pip.move_to(plate.wells_by_name()[well].top(z=5))\n pip.drop_tip()\n else:\n num_asp = pip.max_volume // vol\n chunks = [plate.wells()[i:i+num_asp] for i in range(0,\n len(plate.wells()),\n num_asp)]\n\n pip.pick_up_tip(tips[0].wells()[start_tip-1])\n for chunk in chunks:\n pip.aspirate(num_asp*vol, buffer)\n for well in chunk:\n pip.dispense(vol, well)\n pip.move_to(well.top(z=5))\n pip.drop_tip()\n", + "content": "metadata = {\n 'protocolName': 'Spotsee Well Distribution Protocol',\n 'author': 'Rami Farawi ',\n 'source': 'Custom Protocol Request',\n 'apiLevel': '2.13'\n}\n\n\ndef run(ctx):\n\n [num_cycles, vol, labware, start_tip,\n p20_mount, p300_mount] = get_values( # noqa: F821\n \"num_cycles\", \"vol\", \"labware\", \"start_tip\", \"p20_mount\", \"p300_mount\")\n\n # p20_mount = 'left'\n # vol = 25\n # p300_mount = 'right'\n # labware = '40_well_plate'\n # start_tip = 4\n\n # labware\n reservoir = ctx.load_labware('nest_1_reservoir_195ml', 10)\n plate = ctx.load_labware(labware, 1)\n tips = [ctx.load_labware('opentrons_96_filtertiprack_20ul' if vol < 10 else 'opentrons_96_tiprack_300ul', slot) # noqa: E501\n for slot in [11]]\n\n red_wells = [\n 'J1', 'I1', 'H1', 'G1', 'F1', 'E1', 'D1', 'C1', 'B1', 'A1',\n 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 'J2',\n 'J3', 'I3', 'H3', 'G3', 'F3', 'E3', 'D3', 'C3', 'B3', 'A3',\n 'A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4', 'I4', 'J4',\n 'J5', 'I5', 'H5', 'G5', 'F5', 'E5', 'D5', 'C5', 'B5', 'A5',\n 'A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6', 'I6', 'J6',\n 'J7', 'I7', 'H7', 'G7', 'F7', 'E7', 'D7', 'C7', 'B7', 'A7',\n 'A8', 'B8', 'C8', 'D8', 'E8', 'F8', 'G8', 'H8', 'I8', 'J8'\n ]\n\n # pipettes\n p20 = ctx.load_instrument('p20_single_gen2', p20_mount, tip_racks=tips)\n p300 = ctx.load_instrument('p300_single_gen2', p300_mount, tip_racks=tips)\n pip = p20 if vol < 10 else p300\n\n # mapping\n buffer = reservoir.wells()[0]\n\n # protocol\n pip.pick_up_tip(tips[0].wells()[start_tip-1])\n for _ in range(num_cycles):\n ctx.comment('\\n-------------ADDING BUFFER TO PLATE-------------\\n\\n')\n if labware == '80_well_plate':\n num_asp = pip.max_volume // vol\n chunks = [red_wells[i:i+num_asp] for i in range(0, len(red_wells),\n num_asp)]\n for chunk in chunks:\n pip.aspirate(num_asp*vol, buffer)\n for well in chunk:\n pip.dispense(vol, plate.wells_by_name()[well])\n pip.move_to(plate.wells_by_name()[well].top(z=5))\n\n else:\n num_asp = pip.max_volume // vol\n chunks = [plate.wells()[i:i+num_asp] for i in range(0,\n len(plate.wells()),\n num_asp)]\n\n for chunk in chunks:\n pip.aspirate(num_asp*vol, buffer)\n for well in chunk:\n pip.dispense(vol, well)\n pip.move_to(well.top(z=5))\n ctx.pause('Change Sheet on Deck')\n pip.drop_tip()\n", "custom_labware_defs": [ { "brand": { @@ -1543,9 +1543,15 @@ } ], "fields": [ + { + "default": 20, + "label": "Number of cycles before new tip", + "name": "num_cycles", + "type": "int" + }, { "default": 15, - "label": "Volume to each well (1-20ul)", + "label": "Volume to each well", "name": "vol", "type": "int" }, @@ -1625,10 +1631,10 @@ "type": "nest_1_reservoir_195ml" }, { - "name": "Opentrons 96 Filter Tip Rack 20 \u00b5L on 11", + "name": "Opentrons 96 Tip Rack 300 \u00b5L on 11", "share": false, "slot": "11", - "type": "opentrons_96_filtertiprack_20ul" + "type": "opentrons_96_tiprack_300ul" }, { "name": "Opentrons Fixed Trash on 12", diff --git a/protocols/3e0bef/3e0b3f.ot2.apiv2.py b/protocols/3e0bef/3e0b3f.ot2.apiv2.py index 343649e48..7f825c096 100644 --- a/protocols/3e0bef/3e0b3f.ot2.apiv2.py +++ b/protocols/3e0bef/3e0b3f.ot2.apiv2.py @@ -1,3 +1,4 @@ +# flake8: noqa from opentrons import protocol_api from opentrons import types from opentrons.protocol_api.labware import Well @@ -97,502 +98,504 @@ def run(ctx: protocol_api.ProtocolContext): # load instrument m300 = ctx.load_instrument('p300_multi_gen2', m300_mount, tip_racks=tips) - # extend well objects for improved liquid handling - class WellH(Well): - def __init__(self, well, min_height=0.5, comp_coeff=1.1, - current_volume=0): - super().__init__(well.parent, well._core, APIVersion(2, 13)) - self.well = well - # specified minimum well bottom clearance - self.min_height = min_height - self.comp_coeff = comp_coeff - # specified starting volume in ul - self.current_volume = current_volume - # cross sectional area - if self.diameter is not None: - self.radius = self.diameter/2 - cse = math.pi*(self.radius**2) - elif self.length is not None: - cse = self.length*self.width - else: - cse = None - self.cse = cse - # initial liquid level in mm from start vol - if cse: - self.height = (current_volume/cse) - else: - raise Exception('Labware definition must \ -supply well radius or well length and width.') - if self.height < min_height: - self.height = min_height - elif self.height > well.parent.highest_z: - raise Exception('Specified liquid volume \ -can not exceed the height of the labware.') - - def height_dec(self, vol, ppt, bottom=False): - # decrement height (mm) - dh = (vol/self.cse)*self.comp_coeff - # tip immersion (mm) as fraction of tip length - mm_immersed = 0.05*ppt._tip_racks[0].wells()[0].depth - # decrement til target reaches specified min clearance - self.height = self.height - dh if ( - (self.height - dh - mm_immersed) > self.min_height - ) else self.min_height + mm_immersed - self.current_volume = self.current_volume - vol if ( - self.current_volume - vol > 0) else 0 - tip_ht = self.height - mm_immersed if bottom is False else bottom - return self.well.bottom(tip_ht) - - def height_inc(self, vol, top=False): - # increment height (mm) - ih = (vol/self.cse)*self.comp_coeff - # keep calculated liquid ht between min clearance and well depth - self.height = self.min_height if ( - self.height < self.min_height) else self.height - self.height = (self.height + ih) if ( - (self.height + ih) < self.depth) else self.depth - # increment - self.current_volume += vol - if top is False: - tip_ht = self.height + if not ctx.is_simulating: + + # extend well objects for improved liquid handling + class WellH(Well): + def __init__(self, well, min_height=0.5, comp_coeff=1.1, + current_volume=0): + super().__init__(well.parent, well._core, APIVersion(2, 13)) + self.well = well + # specified minimum well bottom clearance + self.min_height = min_height + self.comp_coeff = comp_coeff + # specified starting volume in ul + self.current_volume = current_volume + # cross sectional area + if self.diameter is not None: + self.radius = self.diameter/2 + cse = math.pi*(self.radius**2) + elif self.length is not None: + cse = self.length*self.width + else: + cse = None + self.cse = cse + # initial liquid level in mm from start vol + if cse: + self.height = (current_volume/cse) + else: + raise Exception('Labware definition must \ + supply well radius or well length and width.') + if self.height < min_height: + self.height = min_height + elif self.height > well.parent.highest_z: + raise Exception('Specified liquid volume \ + can not exceed the height of the labware.') + + def height_dec(self, vol, ppt, bottom=False): + # decrement height (mm) + dh = (vol/self.cse)*self.comp_coeff + # tip immersion (mm) as fraction of tip length + mm_immersed = 0.05*ppt._tip_racks[0].wells()[0].depth + # decrement til target reaches specified min clearance + self.height = self.height - dh if ( + (self.height - dh - mm_immersed) > self.min_height + ) else self.min_height + mm_immersed + self.current_volume = self.current_volume - vol if ( + self.current_volume - vol > 0) else 0 + tip_ht = self.height - mm_immersed if bottom is False else bottom return self.well.bottom(tip_ht) - else: - return self.well.top() - # pipette functions # INCLUDE ANY BINDING TO CLASS - def aspirate_h(self, vol, source, rate=1, bottom=False): - self.aspirate( - vol, source.height_dec(vol, self, bottom=bottom), rate=rate) + def height_inc(self, vol, top=False): + # increment height (mm) + ih = (vol/self.cse)*self.comp_coeff + # keep calculated liquid ht between min clearance and well depth + self.height = self.min_height if ( + self.height < self.min_height) else self.height + self.height = (self.height + ih) if ( + (self.height + ih) < self.depth) else self.depth + # increment + self.current_volume += vol + if top is False: + tip_ht = self.height + return self.well.bottom(tip_ht) + else: + return self.well.top() + + # pipette functions # INCLUDE ANY BINDING TO CLASS + def aspirate_h(self, vol, source, rate=1, bottom=False): + self.aspirate( + vol, source.height_dec(vol, self, bottom=bottom), rate=rate) - def dispense_h(self, vol, dest, rate=1, top=False): - self.dispense(vol, dest.height_inc(vol, top=top), rate=rate) + def dispense_h(self, vol, dest, rate=1, top=False): + self.dispense(vol, dest.height_inc(vol, top=top), rate=rate) - def slow_tip_withdrawal( - self, speed_limit, well_location, to_surface=False): - if self.mount == 'right': - axis = 'A' - else: - axis = 'Z' - previous_limit = None - if axis in ctx.max_speeds.keys(): - for key, value in ctx.max_speeds.items(): - if key == axis: - previous_limit = value - ctx.max_speeds[axis] = speed_limit - if to_surface is False: - self.move_to(well_location.top()) - else: - if isinstance(well_location, WellH): - self.move_to(well_location.bottom().move(types.Point( - x=0, y=0, z=well_location.height+( - 20*(self._tip_racks[0].wells()[0].depth / 88))))) + def slow_tip_withdrawal( + self, speed_limit, well_location, to_surface=False): + if self.mount == 'right': + axis = 'A' else: - self.move_to(well_location.center()) - ctx.max_speeds[axis] = previous_limit - - def custom_pick_up(self, loc=None): - nonlocal t_start - nonlocal t_end - # `custom_pick_up` will pause the protocol when all tip boxes are out - # of tips, prompting the user to replace all tip racks. Once tipracks - # reset, the protocol will start picking up tips from the first tip - # box as defined in the slot order when assigning the labware def - # for that tip box. `pick_up()` will track tips for both pipettes if - # applicable. - - # :param loc: User can manually specify location for tip pick up - - if loc: - self.pick_up_tip(loc) - else: - try: - self.pick_up_tip() - except protocol_api.labware.OutOfTipsError: - flash_lights() - ctx.pause('Replace empty tip racks') - self.reset_tipracks() - t_start = 0 - t_end = int(num_cols) - ctx.set_rail_lights(True) - self.pick_up_tip() - - # bind additional methods to pipettes - for met in [aspirate_h, dispense_h, slow_tip_withdrawal, custom_pick_up]: - setattr( - m300, met.__name__, - MethodType(met, m300)) - - # reagents - liquid_waste = rsvr_1.wells()[0].top() - # cspl = [WellH(well) for well in rsvr_12[0].wells()[:6]] - # for idx in range(num_cols): - # cspl[idx//2].height_inc(720*8*1.1) - - # helper functions - def flash_lights(): - for _ in range(19): - ctx.set_rail_lights(not ctx.rail_lights_on) - ctx.delay(seconds=0.25) - - def flow_rate(asp=92.86, disp=92.86, blow=92.86): - # This function can be used to quickly modify the flow rates of the - # m300. If no parameters are entered, the flow rates will be - # reset. - - # :param asp: Aspiration flow rate, in uL/sec - # :param disp: Dispense flow rate, in uL/sec - - m300.flow_rate.aspirate = asp - m300.flow_rate.dispense = disp - m300.flow_rate.blow_out = blow - - def remove_supernatant(vol, src): - w = int(str(src).split(' ')[0][1:]) - if src.width is not None: - radi = float(src.width)/4 - else: - radi = float(src.diameter)/4 - x0 = radi if w % 2 == 0 else -radi - while vol > 180: - m300.aspirate(180, src.bottom().move(types.Point(x=x0, y=0, z=1))) - m300.dispense(200, liquid_waste) + axis = 'Z' + previous_limit = None + if axis in ctx.max_speeds.keys(): + for key, value in ctx.max_speeds.items(): + if key == axis: + previous_limit = value + ctx.max_speeds[axis] = speed_limit + if to_surface is False: + self.move_to(well_location.top()) + else: + if isinstance(well_location, WellH): + self.move_to(well_location.bottom().move(types.Point( + x=0, y=0, z=well_location.height+( + 20*(self._tip_racks[0].wells()[0].depth / 88))))) + else: + self.move_to(well_location.center()) + ctx.max_speeds[axis] = previous_limit + + def custom_pick_up(self, loc=None): + nonlocal t_start + nonlocal t_end + # `custom_pick_up` will pause the protocol when all tip boxes are out + # of tips, prompting the user to replace all tip racks. Once tipracks + # reset, the protocol will start picking up tips from the first tip + # box as defined in the slot order when assigning the labware def + # for that tip box. `pick_up()` will track tips for both pipettes if + # applicable. + + # :param loc: User can manually specify location for tip pick up + + if loc: + self.pick_up_tip(loc) + else: + try: + self.pick_up_tip() + except protocol_api.labware.OutOfTipsError: + flash_lights() + ctx.pause('Replace empty tip racks') + self.reset_tipracks() + t_start = 0 + t_end = int(num_cols) + ctx.set_rail_lights(True) + self.pick_up_tip() + + # bind additional methods to pipettes + for met in [aspirate_h, dispense_h, slow_tip_withdrawal, custom_pick_up]: + setattr( + m300, met.__name__, + MethodType(met, m300)) + + # reagents + liquid_waste = rsvr_1.wells()[0].top() + # cspl = [WellH(well) for well in rsvr_12[0].wells()[:6]] + # for idx in range(num_cols): + # cspl[idx//2].height_inc(720*8*1.1) + + # helper functions + def flash_lights(): + for _ in range(19): + ctx.set_rail_lights(not ctx.rail_lights_on) + ctx.delay(seconds=0.25) + + def flow_rate(asp=92.86, disp=92.86, blow=92.86): + # This function can be used to quickly modify the flow rates of the + # m300. If no parameters are entered, the flow rates will be + # reset. + + # :param asp: Aspiration flow rate, in uL/sec + # :param disp: Dispense flow rate, in uL/sec + + m300.flow_rate.aspirate = asp + m300.flow_rate.dispense = disp + m300.flow_rate.blow_out = blow + + def remove_supernatant(vol, src): + w = int(str(src).split(' ')[0][1:]) + if src.width is not None: + radi = float(src.width)/4 + else: + radi = float(src.diameter)/4 + x0 = radi if w % 2 == 0 else -radi + while vol > 180: + m300.aspirate(180, src.bottom().move(types.Point(x=x0, y=0, z=1))) + m300.dispense(200, liquid_waste) + m300.blow_out() + m300.aspirate(20, liquid_waste) + vol -= 180 + m300.aspirate(vol, src.bottom().move(types.Point(x=x0, y=0, z=0.7))) + m300.dispense(vol+20, liquid_waste) m300.blow_out() - m300.aspirate(20, liquid_waste) - vol -= 180 - m300.aspirate(vol, src.bottom().move(types.Point(x=x0, y=0, z=0.7))) - m300.dispense(vol+20, liquid_waste) - m300.blow_out() - m300.aspirate(10, liquid_waste) - - def wash(srcs, msg, sup=600): - nonlocal t_start - nonlocal t_end - - if mag_deck.status == 'engaged': - mag_deck.disengage() - ctx.comment(f'Performing wash step: {msg}') - flow_rate() - for idx, (col, src) in enumerate(zip(mag_samps_h, srcs)): - m300.custom_pick_up() - # src = srcs[idx//3] - for i in range(2): - flow_rate(asp=150, disp=150) - if i == 1: - m300.dispense(20, src.top(-1)) - m300.dispense(20, src) - m300.mix(1, 200, src) + m300.aspirate(10, liquid_waste) + + def wash(srcs, msg, sup=600): + nonlocal t_start + nonlocal t_end + + if mag_deck.status == 'engaged': + mag_deck.disengage() + ctx.comment(f'Performing wash step: {msg}') + flow_rate() + for idx, (col, src) in enumerate(zip(mag_samps_h, srcs)): + m300.custom_pick_up() + # src = srcs[idx//3] + for i in range(2): + flow_rate(asp=150, disp=150) + if i == 1: + m300.dispense(20, src.top(-1)) + m300.dispense(20, src) + m300.mix(1, 200, src) + flow_rate() + m300.aspirate(200, src) + m300.slow_tip_withdrawal(10, src, to_surface=True) + m300.dispense(180, col.top(-2)) + flow_rate(asp=10) + m300.aspirate(20, col.top()) + m300.dispense(20, src.top()) flow_rate() - m300.aspirate(200, src) + m300.mix(1, 140, src) + m300.aspirate(140, src) m300.slow_tip_withdrawal(10, src, to_surface=True) - m300.dispense(180, col.top(-2)) - flow_rate(asp=10) - m300.aspirate(20, col.top()) - m300.dispense(20, src.top()) + m300.dispense(140, col) + if not off_deck: + m300.mix(10, 100, col) + ctx.delay(seconds=5) + m300.slow_tip_withdrawal(10, col, to_surface=True) + m300.blow_out() + m300.touch_tip(speed=40) + m300.aspirate(10, col.top()) + m300.drop_tip() + + if off_deck: + flash_lights() + ctx.pause('Please remove plate for manual resuspension') + + mag_deck.engage() + mag_msg = f'Incubating on Mag Deck for {mag_time} minutes' + ctx.delay(minutes=mag_time, msg=mag_msg) + + # Discard Supernatant + ctx.comment(f'Removing supernatant for wash: {msg}') + t_start += num_cols + t_end += num_cols + for src, t_d in zip(mag_samps_h, all_tips[t_start:t_end]): + m300.custom_pick_up() + remove_supernatant(sup, src) + m300.drop_tip(t_d) + + # plate, tube rack maps + init_samps = samp_plate.rows()[0][:num_cols] + mag_samps = mag_plate.rows()[0][:num_cols] + mag_samps_h = [WellH(well) for well in mag_samps] + pcr_samps = pcr_plate.rows()[0][:num_cols] + + # protocol + ctx.set_rail_lights(True) + # # Transfer 700µL CSPL Buffer + 20µL Prot K + # ctx.comment('Transferring 720uL of CSPL Buffer + Proteinase K') + # + # m300.custom_pick_up() + # for idx, col in enumerate(init_samps): + # src = cspl[idx//2] + # for _ in range(4): + # m300.aspirate_h(180, src) + # m300.slow_tip_withdrawal(10, src, to_surface=True) + # m300.dispense(180, col.top(-2)) + # m300.drop_tip() + # + # flash_lights() + # ctx.pause('Please remove samples and incubate at 56C for 30 minutes, \ + # then centrifuge at 4000g for 10 minutes. Once complete, please replace \ + # samples on the deck and place ensure 12-well reservoirs are filled with \ + # necessary reagents in deck slots 2 and 3. When ready, click RESUME.') + # ctx.set_rail_lights(True) + # Creating reagent variables for second part of protocol + rbb = [WellH(well, current_volume=0) for well in rsvr_12[0].wells()[:4]] + cspw1 = [WellH(well) for well in rsvr_12[0].wells()[4:8]] + cspw2 = [WellH(well) for well in rsvr_12[0].wells()[8:]] + spm = [WellH(well) for well in rsvr_12[1].wells()[:8]] + h2o = rsvr_12[1]['A9'] + elution_buffer = [WellH(well) for well in rsvr_12[1].wells()[10:]] + + cspw1_wells = [] + cspw2_wells = [] + spm1_wells = [] + spm2_wells = [] + elution_wells = [] + + for idx in range(num_cols): + rbb[idx//3].height_inc(525*8) + cspw1[idx//3].height_inc(500*8) + cspw2[idx//3].height_inc(500*8) + elution_buffer[idx//6].height_inc(elution_vol*8) + cspw1_wells.append(cspw1[idx//3]) + cspw2_wells.append(cspw2[idx//3]) + elution_wells.append(elution_buffer[idx//6]) + + for idx in range(num_cols*2): + spm[idx//3].height_inc(500*8) + if idx % 2 == 0: + spm1_wells.append(spm[idx//3]) + else: + spm2_wells.append(spm[idx//3]) + + reagent_keys = { + 'RBB + RNase + Mag-Bind Beads': rbb, + 'CSPW 1 Buffer': cspw1, + 'CSPW 2 Buffer': cspw2, + 'Elution Buffer': elution_buffer, + 'SPM': spm, + } + for key in reagent_keys: + for x in reagent_keys[key]: + ctx.comment(f'load {x.current_volume} of {key} in {x}') + # for x in rbb: + # ctx.comment(f'load {x.current_volume} in {x}') + + if samp_labware == 'qiagen_96_tuberack_1200ul': + ctx.comment('Transferring 500uL of sample to plate on MagDeck') + + flow_rate(asp=20) + for src, dest in zip(init_samps, mag_samps_h): + src_asp = src.top(-asp_ht) + m300.custom_pick_up() + for i in range(2): + m300.aspirate(20, src.top()) + m300.aspirate(180, src_asp) + m300.slow_tip_withdrawal(10, src) + m300.dispense_h(180, dest) + m300.slow_tip_withdrawal(10, dest, to_surface=True) + m300.dispense(20, dest.bottom(5)) + m300.aspirate(20, src.top()) + m300.aspirate(140, src_asp) + m300.dispense_h(140, dest) + m300.slow_tip_withdrawal(10, dest, to_surface=True) + m300.dispense(20, dest.bottom(5)) + m300.drop_tip() flow_rate() - m300.mix(1, 140, src) - m300.aspirate(140, src) - m300.slow_tip_withdrawal(10, src, to_surface=True) - m300.dispense(140, col) - if not off_deck: - m300.mix(10, 100, col) - ctx.delay(seconds=5) - m300.slow_tip_withdrawal(10, col, to_surface=True) - m300.blow_out() - m300.touch_tip(speed=40) - m300.aspirate(10, col.top()) - m300.drop_tip() + else: + flash_lights() + ctx.pause('Please make sure samples are loaded on MagDeck') + + # Transfer 5uL RNAse + 500uL RBB buffer + 20uL Mag-Bind Beads + ctx.comment('Transferring 5uL RNAse + 500uL RBB buffer + \ + 20uL Mag-Bind Beads') + + m300.custom_pick_up() + flow_rate(blow=10) + for idx, dest in enumerate(mag_samps): + src = rbb[idx//3] + for _ in range(3): + flow_rate(asp=20, disp=20) + m300.mix(1, 100, src) + m300.aspirate(175, src) + m300.slow_tip_withdrawal(10, src, to_surface=True) + flow_rate(disp=10) + m300.dispense(75, dest.top(-1)) + flow_rate(disp=5) + m300.dispense(50, dest.top(-1)) + ctx.delay(seconds=3) + m300.dispense(30, dest.top(-1)) + ctx.delay(seconds=3) + flow_rate(disp=2) + m300.dispense(20, dest.top(-1)) + m300.blow_out() + m300.touch_tip(speed=40) + m300.drop_tip() + flow_rate() if off_deck: flash_lights() - ctx.pause('Please remove plate for manual resuspension') + ctx.pause('Please incubate samples for 10 minutes post resuspension.') + else: + incubate_msg = f'Incubating at room temperature for {inc_time} \ + minutes plus mixing' + ctx.comment(incubate_msg) + if num_cols > 1: + num_mixes = math.ceil(2*inc_time/num_cols) + else: + num_mixes = 10 + for _ in range(num_mixes): + for col, t_d in zip(mag_samps, all_tips[t_start:t_end]): + if _ == 0: + m300.custom_pick_up() + if not m300.has_tip: + m300.custom_pick_up(t_d) + m300.mix(8, 150, col) + ctx.delay(seconds=1) + m300.blow_out(col.top(-2)) + m300.touch_tip() + if len(mag_samps) > 1: + m300.drop_tip(t_d) + else: + ctx.delay(seconds=30) + if m300.has_tip: + m300.drop_tip(t_d) + + t_start += num_cols + t_end += num_cols mag_deck.engage() mag_msg = f'Incubating on Mag Deck for {mag_time} minutes' ctx.delay(minutes=mag_time, msg=mag_msg) # Discard Supernatant - ctx.comment(f'Removing supernatant for wash: {msg}') - t_start += num_cols - t_end += num_cols + ctx.comment('Removing supernatant') + flow_rate(asp=40, disp=40) for src, t_d in zip(mag_samps_h, all_tips[t_start:t_end]): m300.custom_pick_up() - remove_supernatant(sup, src) + remove_supernatant(540, src) + m300.drop_tip() + m300.custom_pick_up() + remove_supernatant(540, src) m300.drop_tip(t_d) - # plate, tube rack maps - init_samps = samp_plate.rows()[0][:num_cols] - mag_samps = mag_plate.rows()[0][:num_cols] - mag_samps_h = [WellH(well) for well in mag_samps] - pcr_samps = pcr_plate.rows()[0][:num_cols] - - # protocol - ctx.set_rail_lights(True) - # # Transfer 700µL CSPL Buffer + 20µL Prot K - # ctx.comment('Transferring 720uL of CSPL Buffer + Proteinase K') - # - # m300.custom_pick_up() - # for idx, col in enumerate(init_samps): - # src = cspl[idx//2] - # for _ in range(4): - # m300.aspirate_h(180, src) - # m300.slow_tip_withdrawal(10, src, to_surface=True) - # m300.dispense(180, col.top(-2)) - # m300.drop_tip() - # - # flash_lights() - # ctx.pause('Please remove samples and incubate at 56C for 30 minutes, \ - # then centrifuge at 4000g for 10 minutes. Once complete, please replace \ - # samples on the deck and place ensure 12-well reservoirs are filled with \ - # necessary reagents in deck slots 2 and 3. When ready, click RESUME.') - # ctx.set_rail_lights(True) - # Creating reagent variables for second part of protocol - rbb = [WellH(well, current_volume=0) for well in rsvr_12[0].wells()[:4]] - cspw1 = [WellH(well) for well in rsvr_12[0].wells()[4:8]] - cspw2 = [WellH(well) for well in rsvr_12[0].wells()[8:]] - spm = [WellH(well) for well in rsvr_12[1].wells()[:8]] - h2o = rsvr_12[1]['A9'] - elution_buffer = [WellH(well) for well in rsvr_12[1].wells()[10:]] - - cspw1_wells = [] - cspw2_wells = [] - spm1_wells = [] - spm2_wells = [] - elution_wells = [] - - for idx in range(num_cols): - rbb[idx//3].height_inc(525*8) - cspw1[idx//3].height_inc(500*8) - cspw2[idx//3].height_inc(500*8) - elution_buffer[idx//6].height_inc(elution_vol*8) - cspw1_wells.append(cspw1[idx//3]) - cspw2_wells.append(cspw2[idx//3]) - elution_wells.append(elution_buffer[idx//6]) - - for idx in range(num_cols*2): - spm[idx//3].height_inc(500*8) - if idx % 2 == 0: - spm1_wells.append(spm[idx//3]) - else: - spm2_wells.append(spm[idx//3]) - - reagent_keys = { - 'RBB + RNase + Mag-Bind Beads': rbb, - 'CSPW 1 Buffer': cspw1, - 'CSPW 2 Buffer': cspw2, - 'Elution Buffer': elution_buffer, - 'SPM': spm, - } - for key in reagent_keys: - for x in reagent_keys[key]: - ctx.comment(f'load {x.current_volume} of {key} in {x}') - # for x in rbb: - # ctx.comment(f'load {x.current_volume} in {x}') - - if samp_labware == 'qiagen_96_tuberack_1200ul': - ctx.comment('Transferring 500uL of sample to plate on MagDeck') - - flow_rate(asp=20) - for src, dest in zip(init_samps, mag_samps_h): - src_asp = src.top(-asp_ht) - m300.custom_pick_up() - for i in range(2): - m300.aspirate(20, src.top()) - m300.aspirate(180, src_asp) - m300.slow_tip_withdrawal(10, src) - m300.dispense_h(180, dest) - m300.slow_tip_withdrawal(10, dest, to_surface=True) - m300.dispense(20, dest.bottom(5)) - m300.aspirate(20, src.top()) - m300.aspirate(140, src_asp) - m300.dispense_h(140, dest) - m300.slow_tip_withdrawal(10, dest, to_surface=True) - m300.dispense(20, dest.bottom(5)) - m300.drop_tip() flow_rate() - else: - flash_lights() - ctx.pause('Please make sure samples are loaded on MagDeck') - - # Transfer 5uL RNAse + 500uL RBB buffer + 20uL Mag-Bind Beads - ctx.comment('Transferring 5uL RNAse + 500uL RBB buffer + \ -20uL Mag-Bind Beads') - - m300.custom_pick_up() - flow_rate(blow=10) - for idx, dest in enumerate(mag_samps): - src = rbb[idx//3] - for _ in range(3): - flow_rate(asp=20, disp=20) - m300.mix(1, 100, src) - m300.aspirate(175, src) - m300.slow_tip_withdrawal(10, src, to_surface=True) - flow_rate(disp=10) - m300.dispense(75, dest.top(-1)) - flow_rate(disp=5) - m300.dispense(50, dest.top(-1)) - ctx.delay(seconds=3) - m300.dispense(30, dest.top(-1)) - ctx.delay(seconds=3) - flow_rate(disp=2) - m300.dispense(20, dest.top(-1)) - m300.blow_out() - m300.touch_tip(speed=40) - m300.drop_tip() - flow_rate() - - if off_deck: - flash_lights() - ctx.pause('Please incubate samples for 10 minutes post resuspension.') - else: - incubate_msg = f'Incubating at room temperature for {inc_time} \ -minutes plus mixing' - ctx.comment(incubate_msg) - - if num_cols > 1: - num_mixes = math.ceil(2*inc_time/num_cols) - else: - num_mixes = 10 - for _ in range(num_mixes): - for col, t_d in zip(mag_samps, all_tips[t_start:t_end]): - if _ == 0: - m300.custom_pick_up() - if not m300.has_tip: - m300.custom_pick_up(t_d) - m300.mix(8, 150, col) - ctx.delay(seconds=1) - m300.blow_out(col.top(-2)) - m300.touch_tip() - if len(mag_samps) > 1: - m300.drop_tip(t_d) - else: - ctx.delay(seconds=30) - if m300.has_tip: - m300.drop_tip(t_d) - t_start += num_cols - t_end += num_cols - mag_deck.engage() - mag_msg = f'Incubating on Mag Deck for {mag_time} minutes' - ctx.delay(minutes=mag_time, msg=mag_msg) - - # Discard Supernatant - ctx.comment('Removing supernatant') - flow_rate(asp=40, disp=40) - for src, t_d in zip(mag_samps_h, all_tips[t_start:t_end]): - m300.custom_pick_up() - remove_supernatant(540, src) - m300.drop_tip() - m300.custom_pick_up() - remove_supernatant(540, src) - m300.drop_tip(t_d) + # Wash with 500uL CSPW1 Buffer + wash(cspw1_wells, 'CSPW1') - flow_rate() + # Wash with 500uL CSPW2 Buffer + wash(cspw2_wells, 'CSPW2') - # Wash with 500uL CSPW1 Buffer - wash(cspw1_wells, 'CSPW1') + # Wash with SPM Buffer (1) + wash(spm1_wells, 'SPM (first wash)') - # Wash with 500uL CSPW2 Buffer - wash(cspw2_wells, 'CSPW2') + # Wash with SPM Buffer (2) + wash(spm2_wells, 'SPM (second wash)', fin_wash) - # Wash with SPM Buffer (1) - wash(spm1_wells, 'SPM (first wash)') + # Air dry for 10 minutes + mag_deck.engage() + if add_water: + for src in mag_samps_h: + m300.custom_pick_up() + m300.aspirate(100, h2o) + flow_rate(asp=30, disp=30) + m300.dispense(80, src) + ctx.delay(seconds=0.5) + m300.aspirate(100, src) + flow_rate() + m300.dispense(120, liquid_waste) + m300.blow_out() + m300.drop_tip() + + ctx.home() + air_dry_msg = f'Air drying the beads for {air_dry} minutes. \ + Please add elution buffer at 65C to 12-well reservoir.' + ctx.delay(minutes=air_dry, msg=air_dry_msg) + flash_lights() + if not ctx.is_simulating(): + if music: + test_speaker('/var/lib/jupyter/notebooks/mr-blue-sky-ot2.mp3') + else: + test_speaker() + ctx.pause('Please check the Well Plate') - # Wash with SPM Buffer (2) - wash(spm2_wells, 'SPM (second wash)', fin_wash) + mag_deck.disengage() + # Add Elution Buffer + ctx.comment(f'Adding {elution_vol}uL Elution Buffer to samples') - # Air dry for 10 minutes - mag_deck.engage() - if add_water: - for src in mag_samps_h: + for idx, (col, src) in enumerate(zip(mag_samps_h, elution_wells)): m300.custom_pick_up() - m300.aspirate(100, h2o) - flow_rate(asp=30, disp=30) - m300.dispense(80, src) - ctx.delay(seconds=0.5) - m300.aspirate(100, src) - flow_rate() - m300.dispense(120, liquid_waste) - m300.blow_out() + m300.aspirate(elution_vol, src) + m300.slow_tip_withdrawal(10, src, to_surface=True) + m300.dispense_h(elution_vol, col) + if not off_deck: + m300.mix(10, 50, col) + m300.slow_tip_withdrawal(10, col, to_surface=True) + m300.blow_out(col.bottom(6)) + for _ in range(2): + m300.move_to(col.bottom(5)) + m300.move_to(col.bottom(6)) m300.drop_tip() - ctx.home() - air_dry_msg = f'Air drying the beads for {air_dry} minutes. \ -Please add elution buffer at 65C to 12-well reservoir.' - ctx.delay(minutes=air_dry, msg=air_dry_msg) - flash_lights() - if not ctx.is_simulating(): - if music: - test_speaker('/var/lib/jupyter/notebooks/mr-blue-sky-ot2.mp3') + flash_lights() + ctx.home() + if not ctx.is_simulating(): + if music: + test_speaker('/var/lib/jupyter/notebooks/all-i-want-ot2.mp3') + else: + test_speaker() + ctx.pause('Please remove samples and incubate at 65C for 5 minutes.\ + When complete, replace samples and click RESUME') + ctx.set_rail_lights(True) + + # Transfer elution to PCR plate + if not off_deck: + for col in mag_samps_h: + m300.custom_pick_up() + m300.mix(10, 50, col) + m300.slow_tip_withdrawal(10, col, to_surface=True) + m300.blow_out(col.bottom(6)) + for _ in range(2): + m300.move_to(col.bottom(5)) + m300.move_to(col.bottom(6)) + m300.drop_tip(home_after=False) else: - test_speaker() - ctx.pause('Please check the Well Plate') + ctx.comment('Please perform manual resupsension off deck.') - mag_deck.disengage() - # Add Elution Buffer - ctx.comment(f'Adding {elution_vol}uL Elution Buffer to samples') + mag_deck.engage() + mag_msg = 'Incubating on Mag Deck for 3 minutes' + ctx.delay(minutes=3, msg=mag_msg) - for idx, (col, src) in enumerate(zip(mag_samps_h, elution_wells)): - m300.custom_pick_up() - m300.aspirate(elution_vol, src) - m300.slow_tip_withdrawal(10, src, to_surface=True) - m300.dispense_h(elution_vol, col) - if not off_deck: - m300.mix(10, 50, col) - m300.slow_tip_withdrawal(10, col, to_surface=True) - m300.blow_out(col.bottom(6)) - for _ in range(2): - m300.move_to(col.bottom(5)) - m300.move_to(col.bottom(6)) - m300.drop_tip() + ctx.comment(f'Transferring {elution_vol}uL to final PCR plate') + t_start += num_cols + if t_start >= 60: + t_start -= 60 - flash_lights() - ctx.home() - if not ctx.is_simulating(): - if music: - test_speaker('/var/lib/jupyter/notebooks/all-i-want-ot2.mp3') - else: - test_speaker() - ctx.pause('Please remove samples and incubate at 65C for 5 minutes.\ -When complete, replace samples and click RESUME') - ctx.set_rail_lights(True) - - # Transfer elution to PCR plate - if not off_deck: - for col in mag_samps_h: + flow_rate(asp=20) + for src, dest, tip in zip(mag_samps, pcr_samps, all_tips[t_start:]): + w = int(str(src).split(' ')[0][1:]) + if src.width is not None: + radi = float(src.width)/4 + else: + radi = float(src.diameter)/4 + x0 = radi if w % 2 == 0 else -radi m300.custom_pick_up() - m300.mix(10, 50, col) - m300.slow_tip_withdrawal(10, col, to_surface=True) - m300.blow_out(col.bottom(6)) - for _ in range(2): - m300.move_to(col.bottom(5)) - m300.move_to(col.bottom(6)) - m300.drop_tip(home_after=False) - else: - ctx.comment('Please perform manual resupsension off deck.') - - mag_deck.engage() - mag_msg = 'Incubating on Mag Deck for 3 minutes' - ctx.delay(minutes=3, msg=mag_msg) - - ctx.comment(f'Transferring {elution_vol}uL to final PCR plate') - t_start += num_cols - if t_start >= 60: - t_start -= 60 - - flow_rate(asp=20) - for src, dest, tip in zip(mag_samps, pcr_samps, all_tips[t_start:]): - w = int(str(src).split(' ')[0][1:]) - if src.width is not None: - radi = float(src.width)/4 - else: - radi = float(src.diameter)/4 - x0 = radi if w % 2 == 0 else -radi - m300.custom_pick_up() - m300.aspirate( - elution_vol, src.bottom().move(types.Point(x=x0, y=0, z=1))) - m300.dispense(elution_vol, dest) - m300.drop_tip(tip) - - mag_deck.disengage() - ctx.comment('Protocol complete! Please store samples at -20C or \ -continue processing') + m300.aspirate( + elution_vol, src.bottom().move(types.Point(x=x0, y=0, z=1))) + m300.dispense(elution_vol, dest) + m300.drop_tip(tip) + + mag_deck.disengage() + ctx.comment('Protocol complete! Please store samples at -20C or \ + continue processing') diff --git a/protocols/spotsee/fields.json b/protocols/spotsee/fields.json index 9d59663d0..8333cedd4 100644 --- a/protocols/spotsee/fields.json +++ b/protocols/spotsee/fields.json @@ -1,7 +1,13 @@ [ { "type": "int", - "label": "Volume to each well (1-20ul)", + "label": "Number of cycles before new tip", + "name": "num_cycles", + "default": 20 + }, + { + "type": "int", + "label": "Volume to each well", "name": "vol", "default": 15 }, diff --git a/protocols/spotsee/spotsee.ot2.apiv2.py b/protocols/spotsee/spotsee.ot2.apiv2.py index f09502a69..85e72c989 100644 --- a/protocols/spotsee/spotsee.ot2.apiv2.py +++ b/protocols/spotsee/spotsee.ot2.apiv2.py @@ -8,9 +8,9 @@ def run(ctx): - [vol, labware, start_tip, + [num_cycles, vol, labware, start_tip, p20_mount, p300_mount] = get_values( # noqa: F821 - "vol", "labware", "start_tip", "p20_mount", "p300_mount") + "num_cycles", "vol", "labware", "start_tip", "p20_mount", "p300_mount") # p20_mount = 'left' # vol = 25 @@ -21,7 +21,7 @@ def run(ctx): # labware reservoir = ctx.load_labware('nest_1_reservoir_195ml', 10) plate = ctx.load_labware(labware, 1) - tips = [ctx.load_labware('opentrons_96_filtertiprack_20ul' if vol < 20 else 'opentrons_96_tiprack_300ul', slot) # noqa: E501 + tips = [ctx.load_labware('opentrons_96_filtertiprack_20ul' if vol < 10 else 'opentrons_96_tiprack_300ul', slot) # noqa: E501 for slot in [11]] red_wells = [ @@ -38,35 +38,35 @@ def run(ctx): # pipettes p20 = ctx.load_instrument('p20_single_gen2', p20_mount, tip_racks=tips) p300 = ctx.load_instrument('p300_single_gen2', p300_mount, tip_racks=tips) - pip = p20 if vol < 20 else p300 + pip = p20 if vol < 10 else p300 # mapping buffer = reservoir.wells()[0] # protocol - ctx.comment('\n---------------ADDING BUFFER TO PLATE----------------\n\n') - if labware == '80_well_plate': - num_asp = pip.max_volume // vol - chunks = [red_wells[i:i+num_asp] for i in range(0, len(red_wells), - num_asp)] + pip.pick_up_tip(tips[0].wells()[start_tip-1]) + for _ in range(num_cycles): + ctx.comment('\n-------------ADDING BUFFER TO PLATE-------------\n\n') + if labware == '80_well_plate': + num_asp = pip.max_volume // vol + chunks = [red_wells[i:i+num_asp] for i in range(0, len(red_wells), + num_asp)] + for chunk in chunks: + pip.aspirate(num_asp*vol, buffer) + for well in chunk: + pip.dispense(vol, plate.wells_by_name()[well]) + pip.move_to(plate.wells_by_name()[well].top(z=5)) - pip.pick_up_tip(tips[0].wells()[start_tip-1]) - for chunk in chunks: - pip.aspirate(num_asp*vol, buffer) - for well in chunk: - pip.dispense(vol, plate.wells_by_name()[well]) - pip.move_to(plate.wells_by_name()[well].top(z=5)) - pip.drop_tip() - else: - num_asp = pip.max_volume // vol - chunks = [plate.wells()[i:i+num_asp] for i in range(0, - len(plate.wells()), - num_asp)] + else: + num_asp = pip.max_volume // vol + chunks = [plate.wells()[i:i+num_asp] for i in range(0, + len(plate.wells()), + num_asp)] - pip.pick_up_tip(tips[0].wells()[start_tip-1]) - for chunk in chunks: - pip.aspirate(num_asp*vol, buffer) - for well in chunk: - pip.dispense(vol, well) - pip.move_to(well.top(z=5)) - pip.drop_tip() + for chunk in chunks: + pip.aspirate(num_asp*vol, buffer) + for well in chunk: + pip.dispense(vol, well) + pip.move_to(well.top(z=5)) + ctx.pause('Change Sheet on Deck') + pip.drop_tip()