HPInc / HP-Digital-Microfluidics

HP Digital Microfluidics Software Platform and Libraries
MIT License
2 stars 0 forks source link

Assay Support #186

Open EvanKirshenbaum opened 5 months ago

EvanKirshenbaum commented 5 months ago

What is the current support for assays? Are there any assays supported into the code? Are there any examples in the code-base?

Migrated from internal repository. Originally created by Rares Vernica on Jul 05, 2022 at 11:57 AM PDT.
EvanKirshenbaum commented 5 months ago

I'd say that at the moment, there's no support for actual assays (in my limited understanding of the term), as there is no way (yet) to physically determine the composition of a drop (or any other property of it) on any of the supported hardware.

There are three protocols defined (CombSynth, Prepare, and MixPrep), all in pcr.py. These all combine reagents to prepare liquids to be used later or to be analyzed off-line.

Migrated from internal repository. Originally created by @EvanKirshenbaum on Jul 05, 2022 at 2:53 PM PDT.
EvanKirshenbaum commented 5 months ago

Clarifying the question, how do you implement specific recipes. For example:

  1. Take 10ul of liquid A place it in well 1. Take 5ul of liquid B and place it in well 2. (1 could be done manually)
  2. Mix 2ul of B with 4ul of A.
  3. Heat the mix to 90C foe 5min
  4. move this 6ul of mixed A&B under the magnet. Engage the magnet.
  5. move this 6ul to empty well 3 while magnet is still engaged
    Migrated from internal repository. Originally created by Rares Vernica on Jul 05, 2022 at 4:29 PM PDT.
EvanKirshenbaum commented 5 months ago

Clarifying the question, how do you implement specific recipes. For example:

The first two steps are demonstrated in the CombSynth protocol.

1. Take 10ul of liquid A place it in well 1. Take 5ul of liquid B and place it in well 2. (1 could be done manually)

Something like

w1 = board.wells[1]
rA = Reagent.find("A")
w.add(rA, 10*uL)

This returns a future that you can wait() on or hang other actions on. It uses the well's Pipettor, which could be the ManualPipettor, in which case, the user will be prompted to add the reagent and confirm, or the DummyPipettor, in which case it will just pretend to do it.

Alternatively, you could say

w1.contains(rA)
w1.required = 10*uL

Then, when you call Path.dispense_from(w1) and there isn't a drop's worth, it will refill to that level (or to a full well, if that won't fit), before dispensing. (This is what we do in CombSynth.)

2. Mix 2ul of B with 4ul of A.

This is a little less obvious. Assuming that drops are 1uL, you need to dispense two drops from well 1 and four drops from well 2. Then you need to walk all of these drops into a 2x3 grid (actually a 3x7 grid, with spaces in between each pair of drops). One of these is designated the "lead" drop, and it initiates the mixture, and the others join in.

                                .start(self.phase_2_mix.as_process(n_shuttles=n_shuttles,
                                                                   result=result))

and

                               ,join()

The mixing starts when all of the drops have arrived, and drop's step ends when the entire mixing is done.

How it works and how to find the sequence and layout for a particular kind of mixture and dilution is tricky, and we can go over that when I'm given more bandwidth. But the CombSynth protocol gives a number of examples.

The actual mixing sequence is found by calling mixing_sequences.lookup(). In this case, n will be 6, and full will be True (since you want the entire 6 uL of product, rather than a single drop of mixed product).

3. Heat the mix to 90C foe 5min

This should be done as a very simple thermocycle, with a single phase

   phase = ThermocyclePhase("heat", temperature=90*absC, duration=5*minutes)

and run it the way we do in CombSynth.

Unfortunately, single-phase thermocycling doesn't work (see #80).

Failing that, walk the drops onto one of the heating zones, use the Heater.SetTemperature operation to get the temperature to 90*abs_C (an absolute TemperaturePoint), and and then after = 5*minutes have passed, set heater.current_state to OnOff.OFF (to turn it off. Or set the heater's target to None).

Probably, however, you're going to want to keep the drops moving, at least by shuttling them back and forth as we do during thermocycling.

4. move this 6ul of mixed A&B under the magnet. Engage the magnet.

5. move this 6ul to empty well 3 while magnet is still engaged

The hard thing here is that the magnet only affects (or is only supposed to affect) one pad at a time, so you'll have to do it drop by drop. Basically, it's

  1. Move a drop to the pad over the magnet,
  2. Turn the magnet on, by scheduling Magnet.TurnOn
  3. Walk the drop to well 3, and enter it.

Theoretically, you can probably leave the magnet on as you walk other drops over it, but my impressing from talking to Viktor a while ago is that it isn't that straightforward, especially if what you actually want as the product is the stuff that's attached to the beads. Then there's a whole series of washes and buffers and the like. We'll need to talk to the biochemists to understand what they really mean. In any case, at some point, presumably we want to do something with the beads, if only to walk them to a different well, so there will be at least one more liquid involved.

Migrated from internal repository. Originally created by @EvanKirshenbaum on Jul 06, 2022 at 2:34 PM PDT.