Week 3 HW: Lab Automation

Part 1: Opentrons Art

I designed my artwork using the Automation Art GUI at opentrons-art.rcdonovan.com. I uploaded a bat image and the tool pixelated it into dispensing coordinates for three fluorescent proteins: mClover3 (green, 41 points), mRFP1 (red, 393 points), and Azurite (blue, 40 points). The design uses 0.75 µL droplet sizes at 2.2 mm spacing.

Design preview from the GUI:

Screenshot 2026-02-27 at 18.02.52.png Screenshot 2026-02-27 at 18.02.52.png

The GUI generated coordinate lists for each color, which were exported as a complete Python script for the Opentrons OT-2 using the 96 Deep-Well Plate download option. The script uses the Opentrons Python API (v2.20) with a P20 single-channel pipette. For each color, the robot picks up a tip, aspirates fluorescent protein from a deep-well source plate, and dispenses 0.75 µL at each coordinate relative to the center of an agar plate. It automatically refills when the pipette runs dry. Tips are changed between colors to avoid cross-contamination.

Full Opentrons Python Script (click to expand)
from opentrons import types
import string

metadata = {
    'protocolName': 'Abhinav Rajendran - Opentrons Art - HTGAA',
    'author': 'HTGAA',
    'source': 'HTGAA 2026',
    'apiLevel': '2.20'
}

Z_VALUE_AGAR = 2.0
POINT_SIZE = 0.75

mclover3_points = [(-7.7,34.1), (7.7,34.1), (-20.9,25.3), (20.9,25.3), (-20.9,23.1), (20.9,23.1), (-25.3,20.9), (-23.1,20.9), (23.1,20.9), (25.3,20.9), (-20.9,12.1), (20.9,12.1), (-20.9,9.9), (20.9,9.9), (-34.1,7.7), (-31.9,7.7), (-20.9,7.7), (-25.3,5.5), (-23.1,5.5), (23.1,5.5), (25.3,5.5), (-25.3,3.3), (-23.1,3.3), (23.1,3.3), (25.3,3.3), (-27.5,-3.3), (-7.7,-3.3), (27.5,-3.3), (-27.5,-5.5), (-7.7,-5.5), (27.5,-5.5), (-34.1,-7.7), (-31.9,-7.7), (20.9,-7.7), (34.1,-7.7), (-27.5,-16.5), (-27.5,-18.7), (-18.7,-20.9), (-16.5,-20.9), (16.5,-20.9), (18.7,-20.9)]
mrfp1_points = [(-5.5,34.1), (-3.3,34.1), (-1.1,34.1), (1.1,34.1), (3.3,34.1), (5.5,34.1), (-14.3,31.9), (-12.1,31.9), (-9.9,31.9), (9.9,31.9), (12.1,31.9), (14.3,31.9), (-14.3,29.7), (-12.1,29.7), (-9.9,29.7), (9.9,29.7), (12.1,29.7), (14.3,29.7), (-20.9,27.5), (-18.7,27.5), (-16.5,27.5), (16.5,27.5), (18.7,27.5), (20.9,27.5), (-25.3,25.3), (-23.1,25.3), (23.1,25.3), (25.3,25.3), (-25.3,23.1), (-23.1,23.1), (23.1,23.1), (25.3,23.1), (-27.5,20.9), (27.5,20.9), (-27.5,18.7), (27.5,18.7), (-27.5,16.5), (27.5,16.5), (-29.7,14.3), (29.7,14.3), (31.9,14.3), (-29.7,12.1), (29.7,12.1), (31.9,12.1), (-29.7,9.9), (29.7,9.9), (31.9,9.9), (-25.3,7.7), (-23.1,7.7), (-18.7,7.7), (-16.5,7.7), (16.5,7.7), (18.7,7.7), (23.1,7.7), (25.3,7.7), (34.1,7.7), (-34.1,5.5), (-31.9,5.5), (-20.9,5.5), (-18.7,5.5), (-16.5,5.5), (-12.1,5.5), (-9.9,5.5), (-7.7,5.5), (-5.5,5.5), (-3.3,5.5), (-1.1,5.5), (1.1,5.5), (3.3,5.5), (5.5,5.5), (7.7,5.5), (9.9,5.5), (12.1,5.5), (16.5,5.5), (18.7,5.5), (20.9,5.5), (34.1,5.5), (-34.1,3.3), (-31.9,3.3), (-20.9,3.3), (-18.7,3.3), (-16.5,3.3), (-12.1,3.3), (-9.9,3.3), (-7.7,3.3), (-5.5,3.3), (-3.3,3.3), (-1.1,3.3), (1.1,3.3), (3.3,3.3), (5.5,3.3), (7.7,3.3), (9.9,3.3), (12.1,3.3), (16.5,3.3), (18.7,3.3), (20.9,3.3), (34.1,3.3), (-34.1,1.1), (-31.9,1.1), (-18.7,1.1), (-16.5,1.1), (-14.3,1.1), (-12.1,1.1), (-9.9,1.1), (-7.7,1.1), (-5.5,1.1), (-3.3,1.1), (-1.1,1.1), (1.1,1.1), (3.3,1.1), (5.5,1.1), (7.7,1.1), (9.9,1.1), (12.1,1.1), (14.3,1.1), (16.5,1.1), (18.7,1.1), (34.1,1.1), (-34.1,-1.1), (-31.9,-1.1), (-14.3,-1.1), (-5.5,-1.1), (-3.3,-1.1), (-1.1,-1.1), (1.1,-1.1), (3.3,-1.1), (5.5,-1.1), (14.3,-1.1), (34.1,-1.1), (-34.1,-3.3), (-31.9,-3.3), (-25.3,-3.3), (-23.1,-3.3), (-20.9,-3.3), (-18.7,-3.3), (-16.5,-3.3), (-14.3,-3.3), (-12.1,-3.3), (-9.9,-3.3), (-5.5,-3.3), (-3.3,-3.3), (-1.1,-3.3), (1.1,-3.3), (3.3,-3.3), (5.5,-3.3), (7.7,-3.3), (9.9,-3.3), (12.1,-3.3), (14.3,-3.3), (16.5,-3.3), (18.7,-3.3), (20.9,-3.3), (23.1,-3.3), (25.3,-3.3), (34.1,-3.3), (-34.1,-5.5), (-31.9,-5.5), (-25.3,-5.5), (-23.1,-5.5), (-20.9,-5.5), (-18.7,-5.5), (-16.5,-5.5), (-14.3,-5.5), (-12.1,-5.5), (-9.9,-5.5), (-5.5,-5.5), (-3.3,-5.5), (-1.1,-5.5), (1.1,-5.5), (3.3,-5.5), (5.5,-5.5), (7.7,-5.5), (9.9,-5.5), (12.1,-5.5), (14.3,-5.5), (16.5,-5.5), (18.7,-5.5), (20.9,-5.5), (23.1,-5.5), (25.3,-5.5), (34.1,-5.5), (-14.3,-7.7), (-12.1,-7.7), (-9.9,-7.7), (-7.7,-7.7), (-5.5,-7.7), (-3.3,-7.7), (-1.1,-7.7), (1.1,-7.7), (3.3,-7.7), (5.5,-7.7), (7.7,-7.7), (9.9,-7.7), (12.1,-7.7), (14.3,-7.7), (-29.7,-9.9), (-12.1,-9.9), (-9.9,-9.9), (-7.7,-9.9), (-5.5,-9.9), (-3.3,-9.9), (-1.1,-9.9), (1.1,-9.9), (3.3,-9.9), (5.5,-9.9), (7.7,-9.9), (9.9,-9.9), (12.1,-9.9), (29.7,-9.9), (31.9,-9.9), (-29.7,-12.1), (-12.1,-12.1), (-9.9,-12.1), (-7.7,-12.1), (-5.5,-12.1), (-3.3,-12.1), (-1.1,-12.1), (1.1,-12.1), (3.3,-12.1), (5.5,-12.1), (7.7,-12.1), (9.9,-12.1), (12.1,-12.1), (29.7,-12.1), (31.9,-12.1), (-29.7,-14.3), (-12.1,-14.3), (-9.9,-14.3), (-7.7,-14.3), (-5.5,-14.3), (-3.3,-14.3), (-1.1,-14.3), (1.1,-14.3), (3.3,-14.3), (5.5,-14.3), (7.7,-14.3), (9.9,-14.3), (12.1,-14.3), (27.5,-14.3), (29.7,-14.3), (31.9,-14.3), (-12.1,-16.5), (-9.9,-16.5), (-7.7,-16.5), (-5.5,-16.5), (-3.3,-16.5), (-1.1,-16.5), (1.1,-16.5), (3.3,-16.5), (5.5,-16.5), (7.7,-16.5), (9.9,-16.5), (12.1,-16.5), (23.1,-16.5), (25.3,-16.5), (27.5,-16.5), (-12.1,-18.7), (-9.9,-18.7), (-7.7,-18.7), (-5.5,-18.7), (-3.3,-18.7), (-1.1,-18.7), (1.1,-18.7), (3.3,-18.7), (5.5,-18.7), (7.7,-18.7), (9.9,-18.7), (12.1,-18.7), (23.1,-18.7), (25.3,-18.7), (27.5,-18.7), (-27.5,-20.9), (-12.1,-20.9), (-9.9,-20.9), (-7.7,-20.9), (-5.5,-20.9), (-3.3,-20.9), (-1.1,-20.9), (1.1,-20.9), (3.3,-20.9), (5.5,-20.9), (7.7,-20.9), (9.9,-20.9), (12.1,-20.9), (20.9,-20.9), (23.1,-20.9), (25.3,-20.9), (27.5,-20.9), (-25.3,-23.1), (-23.1,-23.1), (-18.7,-23.1), (-16.5,-23.1), (-14.3,-23.1), (-12.1,-23.1), (-9.9,-23.1), (-7.7,-23.1), (-5.5,-23.1), (-3.3,-23.1), (-1.1,-23.1), (1.1,-23.1), (3.3,-23.1), (5.5,-23.1), (7.7,-23.1), (9.9,-23.1), (12.1,-23.1), (14.3,-23.1), (16.5,-23.1), (18.7,-23.1), (20.9,-23.1), (23.1,-23.1), (25.3,-23.1), (-25.3,-25.3), (-23.1,-25.3), (-18.7,-25.3), (-16.5,-25.3), (-14.3,-25.3), (-12.1,-25.3), (-9.9,-25.3), (-7.7,-25.3), (-5.5,-25.3), (-3.3,-25.3), (-1.1,-25.3), (1.1,-25.3), (3.3,-25.3), (5.5,-25.3), (7.7,-25.3), (9.9,-25.3), (12.1,-25.3), (14.3,-25.3), (16.5,-25.3), (18.7,-25.3), (20.9,-25.3), (23.1,-25.3), (25.3,-25.3), (-20.9,-27.5), (-18.7,-27.5), (-16.5,-27.5), (-14.3,-27.5), (-12.1,-27.5), (-9.9,-27.5), (-7.7,-27.5), (-5.5,-27.5), (-3.3,-27.5), (-1.1,-27.5), (1.1,-27.5), (3.3,-27.5), (5.5,-27.5), (7.7,-27.5), (9.9,-27.5), (12.1,-27.5), (14.3,-27.5), (16.5,-27.5), (18.7,-27.5), (20.9,-27.5), (-14.3,-29.7), (-12.1,-29.7), (-9.9,-29.7), (-7.7,-29.7), (-5.5,-29.7), (-3.3,-29.7), (-1.1,-29.7), (1.1,-29.7), (3.3,-29.7), (5.5,-29.7), (7.7,-29.7), (9.9,-29.7), (12.1,-29.7), (14.3,-29.7), (-5.5,-31.9), (-3.3,-31.9), (-1.1,-31.9), (1.1,-31.9), (3.3,-31.9), (5.5,-31.9), (-5.5,-34.1), (-3.3,-34.1), (-1.1,-34.1), (1.1,-34.1), (3.3,-34.1), (5.5,-34.1)]
azurite_points = [(-18.7,14.3), (-16.5,14.3), (-18.7,12.1), (-16.5,12.1), (16.5,12.1), (18.7,12.1), (-18.7,9.9), (-16.5,9.9), (16.5,9.9), (18.7,9.9), (-27.5,7.7), (-1.1,7.7), (1.1,7.7), (20.9,7.7), (27.5,7.7), (-14.3,5.5), (14.3,5.5), (-14.3,3.3), (14.3,3.3), (-20.9,1.1), (20.9,1.1), (-25.3,-1.1), (-23.1,-1.1), (-18.7,-1.1), (-16.5,-1.1), (-7.7,-1.1), (7.7,-1.1), (16.5,-1.1), (18.7,-1.1), (23.1,-1.1), (25.3,-1.1), (-20.9,-7.7), (16.5,-7.7), (18.7,-7.7), (23.1,-14.3), (25.3,-14.3), (-7.7,-31.9), (7.7,-31.9), (-7.7,-34.1), (7.7,-34.1)]

point_name_pairing = [("mclover3", mclover3_points), ("mrfp1", mrfp1_points), ("azurite", azurite_points)]

TIP_RACK_DECK_SLOT = 9
COLORS_DECK_SLOT = 6
AGAR_DECK_SLOT = 5
PIPETTE_STARTING_TIP_WELL = 'A1'

well_colors = {
    'A1': 'sfGFP', 'A2': 'mRFP1', 'A3': 'mKO2', 'A4': 'Venus',
    'A5': 'mKate2_TF', 'A6': 'Azurite', 'A7': 'mCerulean3', 'A8': 'mClover3',
    'A9': 'mJuniper', 'A10': 'mTurquoise2', 'A11': 'mBanana', 'A12': 'mPlum',
    'B1': 'Electra2', 'B2': 'mWasabi', 'B3': 'mScarlet_I', 'B4': 'mPapaya',
    'B5': 'eqFP578', 'B6': 'tdTomato', 'B7': 'DsRed', 'B8': 'mKate2',
    'B9': 'EGFP', 'B10': 'mRuby2', 'B11': 'TagBFP', 'B12': 'mChartreuse_TF',
    'C1': 'mLychee_TF', 'C2': 'mTagBFP2', 'C3': 'mEGFP', 'C4': 'mNeonGreen',
    'C5': 'mAzamiGreen', 'C6': 'mWatermelon', 'C7': 'avGFP', 'C8': 'mCitrine',
    'C9': 'mVenus', 'C10': 'mCherry', 'C11': 'mHoneydew', 'C12': 'TagRFP',
    'D1': 'mTFP1', 'D2': 'Ultramarine', 'D3': 'ZsGreen1', 'D4': 'mMiCy',
    'D5': 'mStayGold2', 'D6': 'PA_GFP'
}

volume_used = {'mclover3': 0, 'mrfp1': 0, 'azurite': 0}

def update_volume_remaining(current_color, quantity_to_aspirate):
    rows = string.ascii_uppercase
    for well, color in list(well_colors.items()):
        if color == current_color:
            if (volume_used[current_color] + quantity_to_aspirate) > 250:
                row = well[0]
                col = well[1:]
                next_row = rows[rows.index(row) + 1]
                next_well = f"{next_row}{col}"
                del well_colors[well]
                well_colors[next_well] = current_color
                volume_used[current_color] = quantity_to_aspirate
            else:
                volume_used[current_color] += quantity_to_aspirate
            break

def run(protocol):
    protocol.home()
    tips_20ul = protocol.load_labware('opentrons_96_tiprack_20ul', TIP_RACK_DECK_SLOT, 'Opentrons 20uL Tips')
    pipette_20ul = protocol.load_instrument("p20_single_gen2", "right", [tips_20ul])
    temperature_plate = protocol.load_labware('nest_96_wellplate_2ml_deep', 6)
    agar_plate = protocol.load_labware('htgaa_agar_plate', AGAR_DECK_SLOT, 'Agar Plate')
    agar_plate.set_offset(x=0.00, y=0.00, z=Z_VALUE_AGAR)
    center_location = agar_plate['A1'].top()
    pipette_20ul.starting_tip = tips_20ul.well(PIPETTE_STARTING_TIP_WELL)

    def dispense_and_jog(pipette, volume, location):
        assert(isinstance(volume, (int, float)))
        above_location = location.move(types.Point(z=location.point.z + 2))
        pipette.move_to(above_location)
        pipette.dispense(volume, location)
        pipette.move_to(above_location)

    def location_of_color(color_string):
        for well, color in well_colors.items():
            if color.lower() == color_string.lower():
                return temperature_plate[well]
        raise ValueError(f"No well found with color {color_string}")

    for i, (current_color, point_list) in enumerate(point_name_pairing):
        if not point_list:
            continue
        pipette_20ul.pick_up_tip()
        max_aspirate = int(18 // POINT_SIZE) * POINT_SIZE
        quantity_to_aspirate = min(len(point_list) * POINT_SIZE, max_aspirate)
        update_volume_remaining(current_color, quantity_to_aspirate)
        pipette_20ul.aspirate(quantity_to_aspirate, location_of_color(current_color))

        for i in range(len(point_list)):
            x, y = point_list[i]
            adjusted_location = center_location.move(types.Point(x, y))
            dispense_and_jog(pipette_20ul, POINT_SIZE, adjusted_location)
            if pipette_20ul.current_volume == 0 and len(point_list[i+1:]) > 0:
                quantity_to_aspirate = min(len(point_list[i:]) * POINT_SIZE, max_aspirate)
                update_volume_remaining(current_color, quantity_to_aspirate)
                pipette_20ul.aspirate(quantity_to_aspirate, location_of_color(current_color))
        pipette_20ul.drop_tip()

AI use: Claude (Anthropic) was used to help document this assignment. The Python script and coordinates were generated by the opentrons-art GUI tool.

Part 2: Post-Lab Questions

Published Paper Using Automation for Biological Applications

Greenhalgh et al. (2024) published “Enabling high-throughput enzyme discovery and engineering with a low-cost, robot-assisted pipeline” in Scientific Reports. The paper describes a generalizable pipeline for high-throughput protein expression and purification using small-scale E. coli cultures and an affordable liquid-handling robot.

The platform purifies 96 proteins in parallel in deep-well plate format. The robot automates the most tedious and error-prone steps: cell lysis, Ni-NTA magnetic bead binding for His-tagged proteins, wash cycles, and elution. Each step was miniaturized from bench-scale protocols to work within 96-well plates, reducing reagent waste and eliminating manual pipetting of hundreds of small volumes. The authors demonstrated reproducibility across replicate experiments and achieved yields up to 400 µg of purified protein per well, which was sufficient for both thermostability and activity assays.

As a test case, the authors used their platform to express and purify the leading PET hydrolases (plastic-degrading enzymes) from the literature. They generated a standardized benchmark dataset comparing these enzymes under identical conditions, something that had not been done before because each enzyme had originally been characterized in a different lab with different protocols. This highlights a key advantage of automation: it removes lab-to-lab variability and lets you make fair comparisons across a protein library.

What makes this paper relevant to protein binder engineering is the generalizability of the approach. The same pipeline could be applied to screen computationally designed protein binders. AI tools like RFdiffusion and ProteinMPNN can now generate hundreds of candidate binder designs in silico, but the experimental bottleneck is expressing and testing them all. A robot-assisted pipeline like this one turns what would be weeks of manual work into a few days of automated runs, closing the gap between computational design throughput and experimental validation throughput.

The platform is built on open-source code (available on GitHub) and uses equipment accessible to most labs, making it a practical model for anyone looking to scale up protein screening.

Reference: Greenhalgh JC, Fahlberg SA, Pfleger BF, Romero PA. Enabling high-throughput enzyme discovery and engineering with a low-cost, robot-assisted pipeline. Sci Rep. 2024;14:14449. doi:10.1038/s41598-024-64938-0

Automation Plan for Final Project

My final project centers on validating computationally designed protein binders through automated experimental screening. The core idea is to use AI protein design tools to generate candidate binders against a target of interest, then use liquid-handling automation to express, purify, and assay them in high throughput.

Here is the automation workflow I would implement:

Step 1: Construct assembly (Opentrons OT-2). Synthesized binder genes (ordered from Twist as clonal fragments) are cloned into an E. coli expression vector using Golden Gate assembly. The OT-2 sets up all reactions in a 96-well plate: picking the correct gene fragment from a source plate, adding vector backbone, BsaI, T4 ligase, and buffer. This eliminates the most tedious manual step of pipetting dozens of small-volume reactions.

Step 2: Transformation and expression (semi-manual). Assembled constructs are transformed into E. coli BL21(DE3) and plated on selective media. After colony picking, cultures are grown in 96-deep-well plates. The OT-2 handles inoculation and induction (adding IPTG at the right OD), ensuring consistent volumes across all 96 wells.

Step 3: Protein purification (Opentrons OT-2). Following the approach from Greenhalgh et al., cells are lysed and His-tagged binder proteins are purified using Ni-NTA magnetic beads in plate format. The robot performs all bead binding, wash, and elution steps. This is where automation saves the most time: manual bead purification of 96 samples takes a full day, while the robot does it in about two hours with better consistency.

Step 4: Binding assay (Opentrons OT-2 + plate reader). Purified binders are dispensed into an ELISA or bio-layer interferometry plate with immobilized target protein. The OT-2 handles serial dilutions for dose-response curves and dispenses detection reagents. A plate reader measures binding signal. Hits are ranked by apparent affinity.

Pseudocode:

for each binder in designed_library:
    # Build
    opentrons.golden_gate(
        insert=binder_gene_fragment,
        vector=pET29b_backbone,
        destination=assembly_plate[binder.index]
    )

    # After transformation, colony picking, growth (semi-manual)

    # Purify (fully automated)
    opentrons.lyse_cells(culture_plate[binder.index])
    opentrons.add_magnetic_beads(lysate_plate[binder.index])
    opentrons.wash(lysate_plate[binder.index], wash_buffer, n=3)
    opentrons.elute(lysate_plate[binder.index], elution_buffer)

    # Assay (fully automated)
    opentrons.serial_dilute(purified_binder, assay_plate, 8_points)
    opentrons.add_target_protein(assay_plate)
    opentrons.add_detection_reagent(assay_plate)
    plate_reader.measure(absorbance_450nm)

# Rank binders by binding signal, feed back to computational model

This pipeline closes the design-build-test-learn loop for AI-designed protein binders. Each round of 96 binders could be screened in about one week, with the computational design of the next round starting immediately from the binding data. For a cloud lab alternative, this workflow maps well onto platforms like Ginkgo Nebula, which could handle all wet lab steps in a fully automated facility at higher throughput but with longer turnaround and higher cost per experiment.

Part 3: Final Project Ideas

Idea 1: De Novo Protein Binder Design Using AI

De Novo Protein Binders Against Snake Venom Three-Finger Toxins: Design synthetic protein binders targeting three-finger toxins (3FTx), the dominant lethal component in elapid snake venoms (cobras, kraits, mambas). 3FTx share a conserved disulfide-rich β-sheet scaffold despite sequence divergence across species, making them a good target for a broad-spectrum binder. Use computational protein design strategies and protein language model embeddings to generate and rank candidate binders against the conserved 3FTx fold. Top candidates validated via automated ELISA binding screen. Snakebite envenoming kills >100,000 people/year and is a WHO-listed neglected tropical disease; current antivenoms are animal-derived, expensive, and species-specific. Computationally designed binders could enable a synthetic, broad-spectrum, low-cost alternative.

Idea 2: AI-Guided Thermostabilisation of Carbonic Anhydrase

Carbonic anhydrase catalyses the reversible hydration of CO₂ (CO₂ + H₂O → HCO₃⁻ + H⁺), a reaction with major industrial applications in carbon capture and sequestration. However, the enzyme denatures at the elevated temperatures found in industrial flue gas streams (>50°C), limiting its practical use. This project would use AI thermostability prediction models — such as ThermoMPNN (a graph neural network trained on ProteinMPNN embeddings to predict ΔΔG° for point mutations) and TemStaPro (which uses protein language model embeddings to classify thermostability across temperature thresholds) — to computationally identify stabilising mutations. The approach would generate a ranked library of single and combinatorial mutants, predict their stability profiles across a temperature range, then experimentally validate top candidates using automated activity assays at increasing temperatures. The goal is to shift the enzyme’s functional temperature window above 60°C while retaining catalytic efficiency.

Idea 3: Improving Cellulase Catalytic Efficiency via Directed Evolution

Engineer a cellulase with improved catalytic efficiency (kcat/Km) for cellulose hydrolysis using a directed evolution approach. Cellulases break down cellulose into fermentable sugars and are a key bottleneck in biofuel production from lignocellulosic biomass. Starting from a wild-type endoglucanase, the project would use error-prone PCR or combinatorial saturation mutagenesis to generate variant libraries, then screen them in 96-well format using an automated DNS (dinitrosalicylic acid) reducing sugar assay on the Opentrons OT-2. Top-performing variants from each round would be recombined and re-screened over multiple DBTL cycles. Unlike the other two projects which are primarily computational design-driven, this project takes a classical directed evolution approach but uses lab automation to dramatically increase screening throughput.

AI use: Claude (Anthropic) was used to help draft and format this homework documentation. The Opentrons art Python script and coordinates were generated by the opentrons-art GUI tool. Web search was used to identify relevant published papers and AI thermostability tools.