Workflow

These scripts provide examples for automating a specific workflow.

Automated bead catching: 1 bead

"""Automated bead catching: 1 bead"""
import bluelake as bl


match_score = bl.timeline["Tracking Match Score"]["Bead 1"]

bl.shutters.clear(1)  # to get rid of any existing bead
bl.microstage.move_to("beads")  # must match the label in the UI
bl.fluidics.open(1, 2, 3, 6)  # list of valves to open matching the UI

match_threshold = 80  # % minimal match score
while match_score.latest_value < match_threshold:
    # We only want to clear the trap if there's a bead there, so > 0% match
    if 0 < match_score.latest_value < match_threshold:
        print("Rejected bead with match score:", match_score.latest_value)
        bl.shutters.clear(1)  # bad bead, BAD bead
    bl.pause(0.5)  # seconds

bl.microstage.move_to("buffer")
bl.fluidics.close(1, 2, 3, 6)

Automated bead catching: 2 beads

"""Automated bead catching: 2 beads"""
import bluelake as bl


match_score1 = bl.timeline["Tracking Match Score"]["Bead 1"]
match_score2 = bl.timeline["Tracking Match Score"]["Bead 2"]
match_scores = [match_score1, match_score2]

target_shutters = [1, 2]
bl.shutters.clear(*target_shutters)  # to get rid of any existing beads

bl.microstage.move_to("beads")  # must match the label in the UI
bl.fluidics.open(1, 2, 3, 6)  # list of valves to open matching the UI

# Note: `match_score1` does not map directly to trap 1. `match_score1` and `match_score2`
# sort beads from left to right, up to down. When `match_score1.latest_value > 0` we can't
# be sure whether that corresponds to the bead in trap 1 or in trap 2. So to catch 2 beads
# we need to make sure both are good or clear both traps even if just one is bad.

match_threshold = 80  # % minimal match score
while any(m.latest_value < match_threshold for m in match_scores):
    if any(0 < m.latest_value < match_threshold for m in match_scores):
        bl.shutters.clear(*target_shutters)  # bad beads
        bl.pause(1)

bl.microstage.move_to("buffer")
bl.fluidics.close(1, 2, 3, 6)

DNA fishing

"""Fish for DNA"""
import bluelake as bl


force1 = bl.timeline["Force LF"]["Trap 1"]
while force1.latest_value < 15:  # [pN]
    bl.mirror1.move_by(dx=-1.0)  # [um]
    bl.mirror1.move_by(dx=+1.0)  # [um]

print("caught molecule")
bl.microstage.move_to("buffer")
bl.fluidics.stop_flow()

Catch 2 beads and fish for DNA

"""Catch 2 beads and then fish for DNA"""
import bluelake as bl


def catch_2_beads(match_threshold=80):
    """Catch 2 beads that satisfy the minimal match threshold in %"""
    match_score1 = bl.timeline["Tracking Match Score"]["Bead 1"]
    match_score2 = bl.timeline["Tracking Match Score"]["Bead 2"]
    match_scores = [match_score1, match_score2]

    target_shutters = [1, 2]
    bl.shutters.clear(*target_shutters)  # to get rid of any existing beads

    bl.microstage.move_to("beads")  # must match the label in the UI
    bl.fluidics.open(1, 2, 3, 6)  # list of valves to open matching the UI

    while any(m.latest_value < match_threshold for m in match_scores):
        if any(0 < m.latest_value < match_threshold for m in match_scores):
            bl.shutters.clear(*target_shutters)  # bad beads
            bl.pause(1)

    bl.microstage.move_to("buffer")
    bl.fluidics.close(1, 2, 3, 6)


def goto_distance(target):
    """Move trap 1 until it reaches the `target` distance from trap 2"""
    distance = bl.timeline["Distance"]["Distance 1"]
    dx = distance.latest_value - target

    while abs(dx) > 0.2:  # um
        if dx <= 0:
            bl.mirror1.move_by(dx=+0.1)
        else:
            bl.mirror1.move_by(dx=-0.1)
        dx = distance.latest_value - target


def fish_for_dna(min_distance_um, max_distance_um, force_threshold):
    """Oscillate trap 1 until the force reaches the given threshold"""
    bl.microstage.move_to("DNA")
    goto_distance(min_distance_um)
    bl.fluidics.open(1, 2, 3, 6)
    bl.pause(1)

    bl.reset_force()
    goto_distance(max_distance_um)
    force2 = bl.timeline["Force LF"]["Trap 2"]
    while force2.latest_value < force_threshold:
        goto_distance(min_distance_um)
        bl.reset_force()
        goto_distance(max_distance_um)

    bl.fluidics.close(1, 2, 3, 6)
    bl.microstage.move_to("buffer")
    goto_distance(min_distance_um)
    bl.reset_force()


catch_2_beads()
fish_for_dna(min_distance_um=10, max_distance_um=10, force_threshold=10)

FRAP (fluorescence recovery after photobleaching)

"""FRAP scan sequence -- scan presets are set from the UI, this script just executes them

Note that the presets contain the scan volume and timings, but not the
laser power -- it's set separately from the script.

Instructions:

1. Set the confocal volume and timings for the initial scan and save it as
   a preset called "reference".
2. Set a new confocal volume and timings and save as "bleach".
3. Save a final preset called "image" to observe the sample after bleaching.
4. Set the laser power for the 3 stages in script below.
5. Run the script.
"""
import bluelake as bl


def increment_frap_count():
    """Add or increment a FRAP counter for the timeline markers"""
    if not hasattr(bl, "frap_count"):
        bl.frap_count = 0
    bl.frap_count += 1


increment_frap_count()
bl.timeline.mark_begin(f"FRAP {bl.frap_count}")

try:
    # Scan 1:
    # Before using this script you must save a scan preset and name it
    # "reference" (or anything else that matches the `start_scan()` below).
    bl.excitation.blue_laser.power_level = 15  # % power
    # and/or: `excitation.red_laser.power_level = 20`  # % power
    bl.confocal.start_scan("reference")
    bl.confocal.wait()

    # Scan 2:
    # As above, this starts a scan preset called "bleach". You must save
    # this preset in the UI before starting the script.
    bl.excitation.blue_laser.power_level = 90  # % power
    bl.confocal.start_scan("bleach")
    bl.confocal.wait()

    # Scan 3:
    # The previous two were single-frame scans, but this one is usually
    # multi-frame (again, set this in the UI and save it). It can also
    # be useful to increase the "Image time" in the UI. Effectively,
    # this adds a pause between frames in order to avoid bleaching if
    # the recovery is slow.
    bl.excitation.blue_laser.power_level = 15  # % power
    bl.confocal.start_scan("image")
    bl.confocal.wait()
finally:
    bl.timeline.mark_end()