Example #1
0
def simulate_without_rl(flags, module):
    flow_params = getattr(module, flags.exp_config).flow_params

    if hasattr(getattr(module, flags.exp_config), "custom_callables"):
        callables = getattr(module, flags.exp_config).custom_callables
    else:
        callables = None
    flow_params['sim'].render = not flags.no_render
    flow_params['simulator'] = 'traci'

    # Specify an emission path if they are meant to be generated.
    if flags.gen_emission:
        flow_params['sim'].emission_path = "./data"

        # Create the flow_params object
        fp_ = flow_params['exp_tag']
        dir_ = flow_params['sim'].emission_path
        with open(os.path.join(dir_, "{}.json".format(fp_)), 'w') as outfile:
            json.dump(flow_params,
                      outfile,
                      cls=FlowParamsEncoder,
                      sort_keys=True,
                      indent=4)

    # Run for the specified number of rollouts.

    flow_params['env'].horizon = 3000
    # Create the experiment object.
    exp = Experiment(flow_params, callables)
    exp.run(flags.num_runs, convert_to_csv=flags.gen_emission)
Example #2
0
 def __init__(self,
              trp,
              vol=15,
              maxdil=16,
              mindilvol=90,
              maxdilvol=100,
              debug=False):
     'Create a new QPCR setup structure'
     self.volume = vol
     self.samples = []
     self.needDil = []
     self.primers = []
     self.nreplicates = []
     self.dilProds = []
     self.reuse = [
     ]  # Index of prior dilution that can be used as input to this one; otherwise None
     self.stages = []
     self.MAXDIL = maxdil
     self.MINDILVOL = mindilvol
     self.MAXDILVOL = maxdilvol
     self.trp = trp
     self.debug = debug
     self.dilutant = decklayout.SSDDIL
     self.jobq = JobQueue()
     self.e = Experiment()
Example #3
0
def train_stable_baselines3(submodule, flags):
    """Train policies using the PPO algorithm in stable-baselines3."""
    from stable_baselines3.common.vec_env import DummyVecEnv
    from stable_baselines3 import PPO
    import torch
    start_time = timeit.default_timer()
    flow_params = submodule.flow_params
    # Path to the saved files
    exp_tag = flow_params['exp_tag']
    result_name = '{}/{}'.format(exp_tag, strftime("%Y-%m-%d-%H:%M:%S"))

    # Perform training.
    print("cuda is available: ", torch.cuda.is_available())
    print('Beginning training.')
    print("==========================================")
    model = run_model_stablebaseline3(flow_params, flags.num_cpus,
                                      flags.rollout_size, flags.num_steps)

    # Save the model to a desired folder and then delete it to demonstrate
    # loading.
    print('Saving the trained model!')
    path = os.path.realpath(os.path.expanduser('~/baseline_results'))
    ensure_dir(path)
    save_path = os.path.join(path, result_name)
    model.save(save_path)
    # dump the flow params
    # check time for choose GPU and CPU
    stop_time = timeit.default_timer()
    run_time = stop_time - start_time
    with open(os.path.join(path, result_name) + '.json', 'w') as outfile:
        json.dump(flow_params,
                  outfile,
                  cls=FlowParamsEncoder,
                  sort_keys=True,
                  indent=4)

    # Replay the result by loading the model
    print('Loading the trained model and testing it out!')
    model.load(save_path)
    flow_params = get_flow_params(os.path.join(path, result_name) + '.json')

    flow_params['sim'].render = False
    flow_params['env'].horizon = 1500  # 150seconds operation
    env = env_constructor(params=flow_params, version=0)()
    # The algorithms require a vectorized environment to run
    eval_env = DummyVecEnv([lambda: env])
    obs = eval_env.reset()
    reward = 0
    for _ in range(flow_params['env'].horizon):
        action, _states = model.predict(obs)
        obs, rewards, dones, info = eval_env.step(action)
        reward += rewards
    print("--------------------------------------------------------")
    flow_params['sim'].render = True
    simulation = Experiment(flow_params)
    simulation.run(num_runs=1)
    print('the final reward is {}'.format(reward))
    print("total run_time:", run_time, "s")
Example #4
0
 def reset(self):
     'Reset this experiment so we can generate it again after adjusting the reagent initial volumes and total time'
     totalTime = clock.elapsed()
     clock.reset(totalTime)
     #print "After reset, elapsed=%d"%clock.elapsed()
     worklist.reset()
     self.e = Experiment()
     self.e.setreagenttemp(6.0)
     self.e.sanitize(3, 50)  # Heavy sanitize
     reagents.reset()
     Sample.clearall()
     decklayout.initWellKnownSamples()
Example #5
0
    def run(self):
        parser=argparse.ArgumentParser(description="TRP")
        parser.add_argument('-v','--verbose',help='Enable verbose output',default=False,action="store_true")
        parser.add_argument('-D','--dewpoint',type=float,help='Dew point',default=10.0)
        args=parser.parse_args()
        
        print "Estimating evaporation for dew point of %.1f C"%args.dewpoint
        globals.dewpoint=args.dewpoint

        self.e=Experiment()
        self.e.setreagenttemp(args.dewpoint)
        self.e.sanitize(3,50)    # Heavy sanitize

        self.setup()
        if args.verbose:
            print '------ Preliminary run to set volume -----'
        else:
            sys.stdout=open(os.devnull,'w')
        self.pgm()
        self.reset()
        self.pgm()
        if args.verbose:
            globals.verbose=True
            print '------ Main run -----'
        else:
            sys.stdout=sys.__stdout__
        self.reset()
        self.pgm()
        self.finish()
Example #6
0
 def reset(self):
     'Reset this experiment so we can generate it again after adjusting the reagent initial volumes and total time'
     totalTime=clock.elapsed()
     clock.reset(totalTime)
     #print "After reset, elapsed=%d"%clock.elapsed()
     worklist.reset()
     self.e=Experiment()
     self.e.setreagenttemp(6.0)
     self.e.sanitize(3,50)    # Heavy sanitize
     reagents.reset()
     Sample.clearall()
     decklayout.initWellKnownSamples()
Example #7
0
    def run(self):
        parser = argparse.ArgumentParser(description="TRP")
        parser.add_argument('-v',
                            '--verbose',
                            help='Enable verbose output',
                            default=False,
                            action="store_true")
        parser.add_argument('-D',
                            '--dewpoint',
                            type=float,
                            help='Dew point',
                            default=10.0)
        args = parser.parse_args()

        print "Estimating evaporation for dew point of %.1f C" % args.dewpoint
        globals.dewpoint = args.dewpoint

        self.e = Experiment()
        self.e.setreagenttemp(args.dewpoint)
        self.e.sanitize(3, 50)  # Heavy sanitize

        self.setup()
        if args.verbose:
            print '------ Preliminary run to set volume -----'
        else:
            sys.stdout = open(os.devnull, 'w')
        self.pgm()
        self.reset()
        self.pgm()
        if args.verbose:
            globals.verbose = True
            print '------ Main run -----'
        else:
            sys.stdout = sys.__stdout__
        self.reset()
        self.pgm()
        self.finish()
Example #8
0
def train_rllib(submodule, flags):
    """Train policies using the PPO algorithm in RLlib."""
    import ray
    from ray.tune import run_experiments

    flow_params = submodule.flow_params
    n_cpus = submodule.N_CPUS
    n_rollouts = submodule.N_ROLLOUTS
    policy_graphs = getattr(submodule, "POLICY_GRAPHS", None)
    policy_mapping_fn = getattr(submodule, "policy_mapping_fn", None)
    policies_to_train = getattr(submodule, "policies_to_train", None)

    alg_run, gym_name, config = setup_exps_rllib(flow_params, n_cpus,
                                                 n_rollouts, policy_graphs,
                                                 policy_mapping_fn,
                                                 policies_to_train, flags)

    ray.init(num_cpus=n_cpus + 1, object_store_memory=200 * 1024 * 1024)
    exp_config = {
        "run": alg_run,
        "env": gym_name,
        "config": {
            **config
        },
        "checkpoint_freq": 20,
        "checkpoint_at_end": True,
        "max_failures": 999,
        "stop": {
            "training_iteration": flags.num_steps,
        },
    }
    print(exp_config["config"]["framework"])
    if flags.checkpoint_path is not None:
        exp_config['restore'] = flags.checkpoint_path
    run_experiments({flow_params["exp_tag"]: exp_config})
    simulation = Experiment(flow_params)
    simulation.run(num_runs=1)
Example #9
0
 def __init__(self, trp, vol=15, maxdil=16, mindilvol=90, maxdilvol=100, debug=False):
     """Create a new QPCR setup structure"""
     self.volume = vol
     self.samples = []
     self.needDil = []
     self.primers = []
     self.nreplicates = []
     self.dilProds = []
     self.reuse = []  # Index of prior dilution that can be used as input to this one; otherwise None
     self.stages = []
     self.MAXDIL = maxdil
     self.MINDILVOL = mindilvol
     self.MAXDILVOL = maxdilvol
     self.trp = trp
     self.debug = debug
     self.dilutant = decklayout.SSDDIL
     self.jobq = JobQueue()
     self.e = Experiment()
Example #10
0
class MSetup(object):
    TGTINVOL = 4

    def __init__(self, trp, vol=15, maxdil=16, mindilvol=90, maxdilvol=100, debug=False):
        """Create a new QPCR setup structure"""
        self.volume = vol
        self.samples = []
        self.needDil = []
        self.primers = []
        self.nreplicates = []
        self.dilProds = []
        self.reuse = []  # Index of prior dilution that can be used as input to this one; otherwise None
        self.stages = []
        self.MAXDIL = maxdil
        self.MINDILVOL = mindilvol
        self.MAXDILVOL = maxdilvol
        self.trp = trp
        self.debug = debug
        self.dilutant = decklayout.SSDDIL
        self.jobq = JobQueue()
        self.e = Experiment()

    def addSamples(self, src, needDil, primers, nreplicates=1, names=None, saveVol=None, saveDil=None, save=True):
        """Add sample(s) to list of qPCRs to do"""
        # print "addSamples(%s)"%src
        if not isinstance(src, list):
            src = [src]
        if save:
            # saveVol is total amount (after dilution) to be immediately saved
            if saveDil is None:
                saveDil = min(needDil, self.MAXDIL)
                if 1 < needDil / saveDil < 2:
                    saveDil = math.sqrt(needDil)
            elif saveDil > needDil:
                logging.warning("addSamples: saveDil=" + saveDil + ", but needDil is only " + needDil)
                saveDil = needDil

            if saveVol is None:
                saveVol = max(self.MINDILVOL * 1.0 / saveDil, self.TGTINVOL)

            if names is None:
                tgt = [Sample(diluteName(src[i].name, saveDil), decklayout.DILPLATE) for i in range(len(src))]
            else:
                tgt = [Sample(diluteName(names[i], saveDil), decklayout.DILPLATE) for i in range(len(src))]
            sv = tgt

            for i in range(len(sv)):
                # print "Save ",src[i]
                svtmp = self.trp.runQPCRDIL(src=[src[i]], vol=saveVol * saveDil, srcdil=saveDil, tgt=[tgt[i]],
                                            dilPlate=True, dilutant=self.dilutant)
                sv[i] = svtmp[0]
        else:
            saveDil = 1
            sv = src

        needDil = needDil / saveDil
        nstages = int(math.ceil(math.log(needDil) / math.log(self.MAXDIL)))
        ndil = len(src) * (nstages + (1 if save else 0))
        logging.notice("QPCR: %dQ/%dD [%s], dilution:%.1fx, primers: [%s]" % (
            len(src) * len(primers) * nreplicates, ndil,
            ",".join([s.name for s in src]) if names is None else ",".join(names), needDil, ",".join(primers)))

        for svi in range(len(sv)):
            s = sv[svi]
            if s.hasBeads:
                prereqs = []
            else:
                j0 = self.jobq.addShake(sample=s, prereqs=[])
                prereqs = [j0]
            intermed = s

            for i in range(nstages):
                dil = math.pow(needDil, 1.0 / nstages)
                # print "stage ",i,", needDil=",needDil,", dil=",dil
                if i > 0:
                    vol = self.MAXDILVOL
                else:
                    vol = min(self.MAXDILVOL * 1.0, max(self.MINDILVOL * 1.0, dil * self.TGTINVOL * 1.0))
                if intermed.plate == decklayout.DILPLATE:
                    firstWell = intermed.well + 4  # Skip by 4 wells at a time to optimize multi-tip movements
                else:
                    firstWell = 0
                if not save and i == 0 and names is not None:
                    # Need to replace the name in this condition
                    dest = Sample(diluteName(names[svi], dil), decklayout.DILPLATE, firstWell=firstWell)
                else:
                    dest = Sample(diluteName(intermed.name, dil), decklayout.DILPLATE, firstWell=firstWell)
                # print "dest=",dest
                j1 = self.jobq.addMultiTransfer(volume=vol * (dil - 1) / dil, src=self.dilutant, dest=dest, prereqs=[])
                prereqs.append(j1)
                j2 = self.jobq.addTransfer(volume=vol / dil, src=intermed, dest=dest, prereqs=prereqs)
                # print "Dilution of %s was %.2f instead of %.2f (error=%.0f%%)"%(dest.name,(dil/(1+dil))/(1/dil),dil,((dil/(1+dil))/(1/dil)/dil-1)*100)
                if dest.hasBeads:
                    prereqs = [j2]
                else:
                    j3 = self.jobq.addShake(sample=dest, prereqs=[j2])
                    prereqs = [j3]
                intermed = dest
            self.dilProds = self.dilProds + [intermed]
            self.primers = self.primers + [primers]
            self.nreplicates = self.nreplicates + [nreplicates]

    def allprimers(self):
        return set([p for sublist in self.primers for p in sublist])

    def addReferences(self, mindil=1, nsteps=6, dstep=4, nreplicates=1, ref=None, primers=None):
        """Add all needed references"""
        # print "addReferences(mindil=",mindil,", nsteps=",nsteps,", dstep=",dstep,", nrep=", nreplicates, ", ref=",ref,")"
        # Make sure the ref reagent is loaded
        if ref is None:
            ref = reagents.getsample("QPCRREF")
        if primers is None:
            primers = self.allprimers()
        dils = [1.0]
        for i in range(nsteps):
            needDil = mindil * math.pow(dstep, i)
            srcDil = 1
            src = [ref]
            for j in range(len(dils)):
                if needDil / dils[j] <= self.MAXDIL:
                    srcDil = dils[j]
                    if srcDil == 1:
                        src = [ref]
                    else:
                        srcname = "%s.D%d" % (ref.name, srcDil)
                        src = [Sample.lookup(srcname)]
                        if src[0] is None:
                            src = [Sample(srcname, decklayout.DILPLATE)]
                    break
            tmp = self.MINDILVOL
            self.MINDILVOL = 75  # Make sure there's enough for resuing dilutions
            self.addSamples(src=src, needDil=needDil / srcDil, primers=primers, nreplicates=nreplicates,
                            save=needDil / srcDil > self.MAXDIL, saveVol=75)
            self.MINDILVOL = tmp
            dils.append(needDil)

        self.addSamples(src=[self.dilutant], needDil=1, primers=primers, nreplicates=nreplicates, save=False)

    def idler(self, t):
        endTime = clock.elapsed() + t
        if self.debug:
            print("Idler(%.0f)" % t)
        while clock.elapsed() < endTime:
            j = self.jobq.getJob()
            if j is None:
                break
            self.jobq.execJob(self.trp.e, j)
        if self.debug:
            print("Idler done with ", endTime - clock.elapsed(), " seconds remaining")

    def run(self):
        """Run the dilutions and QPCR setup"""
        # Setup qPCRs
        # self.jobq.dump()
        self.idler(100000)
        self.trp.e.waitpgm()  # May still need to wait for TC to complete before able to do final jobs
        self.idler(100000)

        if self.jobq.len() > 0:
            logging.error("Blocked jobs remain on queue:")
            self.jobq.dump()
            assert False

        tgt1 = Sample("Barcoded.Mixdown1", decklayout.EPPENDORFS)
        for i in range(1, len(self.dilProds)):
            self.e.transfer(12.5 * 1.2 * 2 / 1.5, self.dilProds[i], tgt1, mix=(False, False))
Example #11
0
class TRP(object):
    def __init__(self):
        'Create a new TRP run'
            
    def reset(self):
        'Reset this experiment so we can generate it again after adjusting the reagent initial volumes and total time'
        totalTime=clock.elapsed()
        clock.reset(totalTime)
        #print "After reset, elapsed=%d"%clock.elapsed()
        worklist.reset()
        self.e=Experiment()
        self.e.setreagenttemp(6.0)
        self.e.sanitize(3,50)    # Heavy sanitize
        reagents.reset()
        Sample.clearall()
        decklayout.initWellKnownSamples()
        
    def addTemplates(self,names,stockconc,finalconc=None,units="nM",plate=decklayout.EPPENDORFS):
        'Add templates as "reagents", return the list of them'
        if finalconc is None:
            print "WARNING: final concentration of template not specified, assuming 0.6x (should add to addTemplates() call"
            [names,stockconc]=listify([names,stockconc])
            finalconc=[0.6*x for x in stockconc]
        else:
            [names,stockconc,finalconc]=listify([names,stockconc,finalconc])

        r=[]
        for i in range(len(names)):
            r.append(reagents.add(names[i],plate=plate,conc=Concentration(stockconc[i],finalconc[i],units),extraVol=30))
        return r
    
    def finish(self):
        self.e.lihahome()
        worklist.userprompt("Process complete. Continue to turn off reagent cooler")
        self.e.setreagenttemp(None)

        #Sample.printallsamples("At completion")
        hasError=False
        for s in Sample.getAllOnPlate():
            if s.volume<1.0 and s.conc is not None and not s.hasBeads:
                print "ERROR: Insufficient volume for ", s," need at least ",1.0-s.volume," ul additional"
                #hasError=True
            elif s.volume<2.5 and s.conc is not None:
                print "WARNING: Low final volume for ", s
            elif s.volume>s.plate.maxVolume:
                print "ERROR: Excess final volume  (",s.volume,") for ",s,", maximum is ",s.plate.maxVolume
                hasError=True
                
        if hasError:
            print "NO OUTPUT DUE TO ERRORS"
            assert(False)
            
        print "Wells used:  samples: %d, dilutions: %d, qPCR: %d"%(Sample.numSamplesOnPlate(decklayout.SAMPLEPLATE),Sample.numSamplesOnPlate(decklayout.DILPLATE),Sample.numSamplesOnPlate(decklayout.QPCRPLATE))
        # Save worklist to a file
        #e.saveworklist("trp1.gwl")
        (scriptname,ext)=os.path.splitext(sys.argv[0])
        self.e.savegem(scriptname+".gem")
        self.e.savesummary(scriptname+".txt")
        Sample.savematlab(scriptname+".m")
        
    ########################
    # Save samples to another well
    ########################
    def saveSamps(self,src,vol,dil,tgt=None,dilutant=None,plate=None,mix=(True,False)):
        [src,vol,dil]=listify([src,vol,dil])
        if plate is None:
            plate=decklayout.REAGENTPLATE
        if tgt is None:
            tgt=[Sample(diluteName(src[i].name,dil[i]),plate) for i in range(len(src))]

        if any([d!=1.0 for d in dil]):
            if dilutant is None:
                dilutant=decklayout.WATER
            self.e.multitransfer([vol[i]*(dil[i]-1) for i in range(len(vol))],dilutant,tgt,(False,False))

        self.e.shakeSamples(src,returnPlate=True)
        for i in range(len(src)):
            self.e.transfer(vol[i],src[i],tgt[i],mix)
            tgt[i].conc=Concentration(1.0/dil[i])
            
        return tgt
    
    def distribute(self,src,dil,vol,wells,tgt=None,dilutant=None,plate=decklayout.SAMPLEPLATE):
        if tgt is None:
            tgt=[Sample("%s.dist%d"%(src[0].name,j),plate) for j in range(wells)]
        
        if dilutant is None:
            dilutant=decklayout.WATER
        self.e.multitransfer([vol*(dil-1) for i in range(wells)],dilutant,tgt)
        self.e.multitransfer([vol for i in range(wells)],src[0],tgt)
        return tgt


    ########################
    # Dilute samples in place
    ########################
    def diluteInPlace(self,tgt,dil=None,finalvol=None):
        # Dilute in place
        # e.g.: trp.diluteInPlace(tgt=rt1,dil=2)
        [tgt,dil,finalvol]=listify([tgt,dil,finalvol])
        dilutant=decklayout.WATER
        for i in range(len(tgt)):
            if finalvol[i] is not None and dil[i] is None:
                self.e.transfer(finalvol[i]-tgt[i].volume,dilutant,tgt[i],mix=(False,False))
            elif finalvol[i] is None and dil[i] is not None:
                self.e.transfer(tgt[i].volume*(dil[i]-1),dilutant,tgt[i],mix=(False,False))
            else:
                print "diluteInPlace: cannot specify both dil and finalvol"
                assert(False)
        #print "after dilute, tgt[0]=",str(tgt[0]),",mixed=",tgt[0].isMixed()
        return tgt   #  The name of the samples are unchanged -- the predilution names

    ########################
    # Run a reaction in place
    ########################
    def runRxInPlace(self,src,vol,master,returnPlate=True,finalx=1.0):
        'Run reaction on beads in given total volume'
        [vol,src,master]=listify([vol,src,master])
        mastervol=[vol[i]*finalx/master[i].conc.dilutionneeded() for i in range(len(vol))]
        watervol=[vol[i]-src[i].volume-mastervol[i] for i in range(len(vol))]
        if any([w < -0.01 for w in watervol]):
            print "runRxInPlace: negative amount of water needed: ",w
            assert(False)
        for i in range(len(src)):
            if  watervol[i]>0:
                self.e.transfer(watervol[i],decklayout.WATER,src[i],(False,False))
        for i in range(len(src)):
            self.e.transfer(mastervol[i],master[i],src[i],(True,src[i].hasBeads))
        self.e.shakeSamples(src,returnPlate=returnPlate)

    ########################
    # T7 - Transcription
    ########################
    def runT7Setup(self,theo,src,vol,srcdil,tgt=None,rlist=["MT7"]):
        [theo,src,tgt,srcdil]=listify([theo,src,tgt,srcdil])
        for i in range(len(src)):
            if tgt[i] is None:
                if theo[i]:
                    tgt[i]=Sample("%s.T+"%src[i].name,decklayout.SAMPLEPLATE)
                else:
                    tgt[i]=Sample("%s.T-"%src[i].name,decklayout.SAMPLEPLATE)


        worklist.comment("runT7: source=%s"%[str(s) for s in src])

        rvols=[reagents.getsample(x).conc.volneeded(vol) for x in rlist]
        rtotal=sum(rvols)
        sourcevols=[vol*1.0/s for s in srcdil]
        if any(theo):
            theovols=[(vol*1.0/reagents.getsample("Theo").conc.dilutionneeded() if t else 0) for t in theo]
            watervols=[vol-theovols[i]-sourcevols[i]-rtotal for i in range(len(src))]
        else:
            watervols=[vol-sourcevols[i]-rtotal for i in range(len(src))]

        if any([w<-1e-10 for w in watervols]):
            print "runT7Setup: Negative amount of water required: ",watervols
            assert False
        if sum(watervols)>0.01:
            self.e.multitransfer(watervols,decklayout.WATER,tgt)
        for ir in range(len(rlist)):
            self.e.multitransfer([rvols[ir] for s in tgt],reagents.getsample(rlist[ir]),tgt)
        if any(theo):
            self.e.multitransfer([tv for tv in theovols if tv>0.01],reagents.getsample("Theo"),[tgt[i] for i in range(len(theovols)) if theovols[i]>0],ignoreContents=True)
        for i in range(len(src)):
            self.e.transfer(sourcevols[i],src[i],tgt[i])
        self.e.shakeSamples(tgt,returnPlate=True)
        for t in tgt:
            t.ingredients['BIND']=1e-20*sum(t.ingredients.values())
        return tgt
    
    def runT7Pgm(self,vol,dur):
        if dur<100:
            pgm="TRP37-%d"%dur
        else:
            pgm="T37-%d"%dur
        worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@37,%d TEMP@25,2'%(pgm,dur*60))
        self.e.runpgm(pgm,dur, False,vol)

    def runT7Stop(self,theo,tgt,stopmaster=None,srcdil=2):
        [theo,tgt,stopmaster,srcdil]=listify([theo,tgt,stopmaster,srcdil])
        if stopmaster is None:
            stopmaster=["MStpS_NT" if t==0 else "MStpS_WT" for t in theo]
            
        # Adjust source dilution
        for i in range(len(tgt)):
            tgt[i].conc=Concentration(srcdil[i],1)

        ## Stop
        sstopmaster=[reagents.getsample(s) for s in stopmaster]
        for i in range(len(tgt)):
            stopvol=tgt[i].volume/(sstopmaster[i].conc.dilutionneeded()-1)
            finalvol=tgt[i].volume+stopvol
            self.e.transfer(finalvol-tgt[i].volume,sstopmaster[i],tgt[i])
            
        self.e.shakeSamples(tgt,returnPlate=True)

        return tgt
    
    def runT7(self,theo,src,vol,srcdil,tgt=None,dur=15,stopmaster=None):
        [theo,src,tgt,srcdil,stopmaster]=listify([theo,src,tgt,srcdil,stopmaster])
        tgt=self.runT7Setup(theo,src,vol,srcdil,tgt)
        self.runT7Pgm(vol,dur)
        tgt=self.runT7Stop(theo,tgt,stopmaster)
        return tgt

    ########################
    # Beads
    ########################
    def bindBeads(self,src,beads=None,beadConc=None,bbuffer=None,incTime=60,addBuffer=False):
        if beads is None:
            beads=reagents.getsample("Dynabeads")
        if bbuffer is None:
            bbuffer=reagents.getsample("BeadBuffer")
            
        [src,beads,bbuffer,beadConc]=listify([src,beads,bbuffer,beadConc])

        for s in src:
            if s.plate!=decklayout.SAMPLEPLATE:
                print "runBeadCleanup: src ",s," is not in sample plate."
                assert(0)
            s.conc=None		# Can't track concentration of beads
            
        self.e.moveplate(src[0].plate,"Home")		# Make sure we do this off the magnet

        # Calculate volumes needed
        beadConc=[beads[i].conc.final if beadConc[i] is None else beadConc[i] for i in range(len(beads))]
        beadDil=beads[i].conc.stock/beadConc[i]
        if addBuffer:
            totalvol=[s.volume/(1-1.0/beadDil-1.0/bbuffer[i].conc.dilutionneeded()) for s in src]
            buffervol=[totalvol[i]/bbuffer[i].conc.dilutionneeded() for i in range(len(src))]
            # Add binding buffer to bring to 1x (beads will already be in 1x, so don't need to provide for them)
            for i in range(len(src)):
                self.e.transfer(buffervol[i],bbuffer[i],src[i])
        else:
            buffervol=[0.0 for i in range(len(src))]
            totalvol=[s.volume/(1-1.0/beadDil) for s in src]

        beadvol=[t/beadDil for t in totalvol]

        # Transfer the beads
        for i in range(len(src)):
            self.e.transfer(beadvol[i],beads[i],src[i],(True,True))	# Mix beads after (before mixing handled automatically by sample.py)

        self.e.shake(src[0].plate,dur=incTime,returnPlate=False)

    def sepWait(self,src,sepTime=None):
        if sepTime is None:
            maxvol=max([s.volume for s in src])
            if maxvol > 50:
                sepTime=50
            else:
                sepTime=30
        self.e.pause(sepTime)	# Wait for separation
        
    def beadWash(self,src,washTgt=None,sepTime=None,residualVolume=10,keepWash=False,numWashes=2,wash=None,washVol=50,keepFinal=False,finalTgt=None,keepVol=4.2,keepDil=5):
        # Perform washes
        # If keepWash is true, retain all washes (combined)
        # If keepFinal is true, take a sample of the final wash (diluted by keepDil)
        if wash is None:
            wash=decklayout.WATER
        [src,wash]=listify([src,wash])
        # Do all washes while on magnet
        assert(len(set([s.plate for s in src]))==1)	# All on same plate
        if keepWash:
            if washTgt is None:
                washTgt=[]
                for i in range(len(src)):
                    if s[i].volume-residualVolume+numWashes*(washVol-residualVolume) > decklayout.DILPLATE.maxVolume-20:
                        print "Saving %.1f ul of wash in eppendorfs"%(numWashes*washVol)
                        washTgt.append(Sample("%s.Wash"%src[i].name,decklayout.EPPENDORFS))
                    else:
                        washTgt.append(Sample("%s.Wash"%src[i].name,decklayout.DILPLATE))

        if keepFinal:
            if finalTgt is None:
                finalTgt=[]
                for i in range(len(src)):
                    finalTgt.append(Sample("%s.Final"%src[i].name,decklayout.DILPLATE))

        if any([s.volume>residualVolume for s in src]):
            # Separate and remove supernatant
            self.e.moveplate(src[0].plate,"Magnet")	# Move to magnet
            self.sepWait(src,sepTime)

            # Remove the supernatant
            for i in range(len(src)):
                if src[i].volume > residualVolume:
                    amt=src[i].amountToRemove(residualVolume)
                    if keepWash:
                        self.e.transfer(amt,src[i],washTgt[i])	# Keep supernatants
                        washTgt[i].conc=None	# Allow it to be reused
                    else:
                        self.e.dispose(amt,src[i])	# Discard supernatant
                
        # Wash

        for washnum in range(numWashes):
            self.e.moveplate(src[0].plate,"Home")
            if keepFinal and washnum==numWashes-1:
                'Retain sample of final'
                for i in range(len(src)):
                    src[i].conc=None
                    self.e.transfer(washVol-src[i].volume,wash[i],src[i],mix=(False,True))	# Add wash
                self.e.shake(src[0].plate,returnPlate=True)
                self.saveSamps(src=src,tgt=finalTgt,vol=keepVol,dil=keepDil,plate=decklayout.DILPLATE)
            else:
                for i in range(len(src)):
                    src[i].conc=None
                    self.e.transfer(washVol-src[i].volume,wash[i],src[i],mix=(False,False))	# Add wash, no need to pipette mix since some heterogenity won't hurt here
                self.e.shake(src[0].plate,returnPlate=False)

            self.e.moveplate(src[0].plate,"Magnet")	# Move to magnet
                
            self.sepWait(src,sepTime)
                
            for i in range(len(src)):
                amt=src[i].amountToRemove(residualVolume)
                if keepWash:
                    self.e.transfer(amt,src[i],washTgt[i])	# Remove wash
                    washTgt[i].conc=None	# Allow it to be reused
                else:
                    self.e.dispose(amt,src[i])	# Remove wash

        self.e.moveplate(src[0].plate,"Home")

        # Should only be residualVolume left with beads now
        result=[]
        if keepWash:
            result=result+washTgt
        if keepFinal:
            result=result+finalTgt

        return result

    def beadAddElutant(self,src,elutant=None,elutionVol=30,eluteTime=60,returnPlate=True,temp=None):
        if elutant is None:
            elutant=decklayout.WATER
        [src,elutionVol,elutant]=listify([src,elutionVol,elutant])
        for i in range(len(src)):
            if elutionVol[i]<30:
                print "WARNING: elution from beads with %.1f ul < minimum of 30ul"%elutionVol[i]
                print "  src=",src[i]
            self.e.transfer(elutionVol[i]-src[i].volume,elutant[i],src[i],(False,True))	
        if temp is None:
            self.e.shake(src[0].plate,dur=eluteTime,returnPlate=returnPlate)
        else:
            self.e.shake(src[0].plate,dur=30,returnPlate=False)
            worklist.pyrun('PTC\\ptcsetpgm.py elute TEMP@%d,%d TEMP@25,2'%(temp,eluteTime))
            self.e.runpgm("elute",eluteTime/60,False,elutionVol[0])
            if returnPlate:
                self.e.moveplate(src[0].plate,"Home")

    def beadSupernatant(self,src,tgt=None,sepTime=None,residualVolume=10,plate=None):
        if plate is None:
            plate=decklayout.SAMPLEPLATE
        if tgt is None:
            tgt=[]
            for i in range(len(src)):
                tgt.append(Sample("%s.SN"%src[i].name,plate))
        [src,tgt]=listify([src,tgt])

        if any([s.plate!=src[0].plate for s in src]):
            print "beadSupernatant: Attempt to magsep on multiple plates at the same time"
            assert False

        self.e.moveplate(src[0].plate,"Magnet")	# Move to magnet
        self.sepWait(src,sepTime)

        for i in range(len(src)):
            self.e.transfer(src[i].amountToRemove(residualVolume),src[i],tgt[i],(False,False))	# Transfer elution to new tube

        self.e.moveplate(src[0].plate,"Home")
        return tgt

    def beadCombine(self,src,residualVolume=10,suspendVolume=150,sepTime=None):
        'Combine everything in the src wells into a the first well; assumes that there are enough beads in that well for all the combination'
        tgt=src[0]
        for s in src[1:]:
            # Combine s with tgt
            if tgt.volume>residualVolume:
                self.e.moveplate(tgt.plate,"Magnet")	# Move to magnet
                self.sepWait([tgt],sepTime)
                self.e.dispose(tgt.amountToRemove(residualVolume),tgt)
            self.e.moveplate(tgt.plate,"Home")	
            if s.volume<suspendVolume:
                self.e.transfer(suspendVolume-s.volume,decklayout.WATER,s,(False,False))
            vol=s.volume-residualVolume-1
            s.conc=None
            self.e.transfer(vol,s,tgt,mix=(True,True))

        self.e.moveplate(tgt.plate,"Home")	
        return src[0:1]
    
    ########################
    # RT - Reverse Transcription
    ########################
    def runRT(self,src,vol,srcdil,tgt=None,dur=20,heatInactivate=False):
        result=self.runRTSetup(src,vol,srcdil,tgt)
        self.runRTPgm(dur,heatInactivate=heatInactivate)
        return result
    
    def runRTInPlace(self,src,vol,dur=20,heatInactivate=False):
        'Run RT on beads in given volume'

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=None

        self.runRxInPlace(src,vol,reagents.getsample("MPosRT"),returnPlate=False)
        self.runRTPgm(dur,heatInactivate=heatInactivate)
        
    def runRTSetup(self,src,vol,srcdil,tgt=None,rtmaster=None):
        if rtmaster is None:
            rtmaster=reagents.getsample("MPosRT")
        if tgt is None:
            tgt=[Sample(s.name+".RT+",decklayout.SAMPLEPLATE) for s in src]

        [src,tgt,vol,srcdil]=listify([src,tgt,vol,srcdil])

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=Concentration(srcdil[i],1)
            
        self.e.stage('RTPos',[rtmaster],[src[i] for i in range(len(src)) ],[tgt[i] for i in range(len(tgt)) ],[vol[i] for i in range(len(vol))],destMix=False)
        #self.e.shakeSamples(tgt,returnPlate=True)
        return tgt

    def runRTPgm(self,dur=20,heatInactivate=False):
        if heatInactivate:
            hidur=2
            pgm="RT-%d"%dur
            worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@37,%d TEMP@95,%d TEMP@25,2 RATE 0.5'%(pgm,dur*60,hidur*60))
            self.e.runpgm(pgm,dur+hidur+2.5,False,100)		# Volume doesn't matter since it's just an incubation, use 100ul
        else:
            if dur<100:
                pgm="TRP37-%d"%dur
            else:
                pgm="T37-%d"%dur
            worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@37,%d TEMP@25,2'%(pgm,dur*60))
            self.e.runpgm(pgm,dur,False,100)		# Volume doesn't matter since it's just an incubation, use 100ul
 
    ########################
    # Lig - Ligation
    ########################
    def runLig(self,prefix=None,src=None,vol=None,srcdil=None,tgt=None,master=None,anneal=True,ligtemp=25):
        if master is None:
            master=[reagents.getsample("MLigAN7") if p=='A' else reagents.getsample("MLigBN7") for p in prefix]

        #Extension
        [src,tgt,vol,srcdil,master]=listify([src,tgt,vol,srcdil,master])
        if tgt is None:
            tgt=[Sample("%s.%s"%(src[i].name,master[i].name),decklayout.SAMPLEPLATE) for i in range(len(src))]

        # Need to check since an unused ligation master mix will not have a concentration
        minsrcdil=1/(1-1/master[0].conc.dilutionneeded()-1/reagents.getsample("MLigase").conc.dilutionneeded())
        for i in srcdil:
            if i<minsrcdil:
                print "runLig: srcdil=%.2f, but must be at least %.2f based on concentrations of master mixes"%(i,minsrcdil)
                assert(False)

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=Concentration(srcdil[i],1)

        i=0
        while i<len(tgt):
            lasti=i+1
            while lasti<len(tgt) and master[i]==master[lasti]:
                lasti=lasti+1
            self.e.stage('LigAnneal',[master[i]],src[i:lasti],tgt[i:lasti],[vol[j]/1.5 for j in range(i,lasti)],1.5,destMix=False)
            i=lasti
            
        if anneal:
            self.e.shakeSamples(tgt,returnPlate=False)
            self.e.runpgm("TRPANN",5,False,max(vol),hotlidmode="CONSTANT",hotlidtemp=100)
        self.e.stage('Ligation',[reagents.getsample("MLigase")],[],tgt,vol,destMix=False)
        self.e.shakeSamples(tgt,returnPlate=False)
        self.runLigPgm(max(vol),ligtemp)
        return tgt
        
    def runLigPgm(self,vol,ligtemp,inactivate=True,inacttemp=65):
        if inactivate:
            pgm="LIG15-%.0f"%ligtemp
            worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@%.0f,900 TEMP@%.0f,600 TEMP@25,30'%(pgm,ligtemp,inacttemp))
            self.e.runpgm(pgm,27,False,vol,hotlidmode="TRACKING",hotlidtemp=10)
        elif ligtemp==25:
            worklist.comment('Ligation at room temp')
            self.e.pause(15*60)
        else:
            pgm="TRP%.0f-15"%ligtemp
            worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@%.0f,900 TEMP@25,30'%(pgm,ligtemp))
            self.e.runpgm(pgm,17,False,vol,hotlidmode="TRACKING",hotlidtemp=10)

    def runLigInPlace(self,src,vol,ligmaster,anneal=True,ligtemp=25):
        'Run ligation on beads'
        [vol,src]=listify([vol,src])
        annealvol=[v*(1-1/reagents.getsample("MLigase").conc.dilutionneeded()) for v in vol]

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=None

        self.runRxInPlace(src,annealvol,reagents.getsample(ligmaster),returnPlate=not anneal,finalx=1.5)
        if anneal:
            self.e.runpgm("TRPANN",5,False,max([s.volume for s in src]),hotlidmode="CONSTANT",hotlidtemp=100)

        ## Add ligase
        self.runRxInPlace(src,vol,reagents.getsample("MLigase"),returnPlate=False)
        self.runLigPgm(max(vol),ligtemp,inactivate=False)	# Do not heat inactivate since it may denature the beads

    ########################
    # Incubation - run a single temp incubation followed by inactivation
    ########################
    def runIncubation(self,src=None,vol=None,srcdil=None,tgt=None,enzymes=None,incTemp=37,incTime=15,hiTemp=None,hiTime=0,inPlace=False):
        if len(enzymes)!=1:
            print "ERROR: runIncubation only supports a single master mix"
            assert False
        if inPlace:
            if tgt is not None:
                print "ERROR: tgt specified for in-place incubation"
                assert False
        elif tgt is None:
            tgt=[Sample("%s.%s"%(src[i].name,enzymes[0].name),decklayout.SAMPLEPLATE) for i in range(len(src))]

        if srcdil==None:
            # Minimum dilution (no water)
            srcdil=1/(1-sum([1/e.conc.dilutionneeded() for e in enzymes]))

        if vol is None and inPlace:
            vol=[s.volume*srcdil for s in src]
            
        [src,tgt,vol,srcdil]=listify([src,tgt,vol,srcdil])

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=Concentration(srcdil[i],1)

        if inPlace:
            self.runRxInPlace(src,vol,enzymes[0],returnPlate=False)
            tgt=src
        else:
            self.e.stage('User',enzymes,src,tgt,vol,destMix=False)
            self.e.shakeSamples(tgt,returnPlate=False)

        if hiTemp is None:
            worklist.pyrun('PTC\\ptcsetpgm.py INC TEMP@%.0f,%.0f TEMP@25,30'%(incTemp,incTime*60))
        else:
            assert(hiTime>0)
            worklist.pyrun('PTC\\ptcsetpgm.py INC TEMP@%.0f,%.0f TEMP@%.0f,%.0f TEMP@25,30'%(incTemp,incTime*60,hiTemp,hiTime*60))
        self.e.runpgm("INC",incTime+hiTime+2,False,max(vol),hotlidmode="TRACKING",hotlidtemp=10)
        return tgt

    ########################
    # USER - USER enzyme digestion
    ########################
    def runUser(self,src=None,vol=None,srcdil=None,tgt=None,incTime=15,inPlace=False):
        return self.runIncubation(src=src,vol=vol,srcdil=srcdil,tgt=tgt,incTemp=37,incTime=incTime,enzymes=[reagents.getsample("MUser")],inPlace=inPlace)
        
    ########################
    # Klenow extension
    ########################
    def runKlenow(self,src=None,vol=None,srcdil=None,tgt=None,incTime=15,hiTime=20,inPlace=False):
        assert(inPlace or vol is not None)
        return self.runIncubation(src=src,vol=vol,srcdil=srcdil,tgt=tgt,incTemp=37,incTime=incTime,hiTemp=75,hiTime=hiTime,enzymes=[reagents.getsample("MKlenow")],inPlace=inPlace)

    ########################
    # DNase digestion
    ########################
    def runDNase(self,src=None,vol=None,srcdil=None,tgt=None,incTime=15,hiTime=10,inPlace=False):
        return self.runIncubation(src=src,vol=vol,srcdil=srcdil,tgt=tgt,incTemp=37,incTime=incTime,hiTemp=75,hiTime=hiTime,enzymes=[reagents.getsample("MDNase")],inPlace=inPlace)

    ########################
    # PCR
    ########################
    def runPCR(self,prefix,src,vol,srcdil,tgt=None,ncycles=20,suffix='S',sepPrimers=True,primerDil=4):
        ## PCR
        [prefix,src,tgt,vol,srcdil,suffix]=listify([prefix,src,tgt,vol,srcdil,suffix])
        for i in range(len(tgt)):
            if tgt[i] is None:
                tgt[i]=Sample("%s.P%s%s"%(src[i].name,prefix[i],suffix[i]),src[i].plate)

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=Concentration(srcdil[i],1)
        
        if sepPrimers:
            sampvols=[vol[i]/srcdil[i] for i in range(len(src))]
            mm=reagents.getsample("MPCR")
            mmvols=[vol[i]/mm.conc.dilutionneeded() for i in range(len(src))]
            for s in prefix + suffix:
                if not reagents.isReagent(s):
                    reagents.add(name=s,conc=primerDil,extraVol=30)

            sprefix=[reagents.getsample(p) for p in prefix]
            ssuffix=[reagents.getsample(p) for p in suffix]

            prefixvols=[vol[i]/sprefix[i].conc.dilutionneeded() for i in range(len(src))]
            suffixvols=[vol[i]/ssuffix[i].conc.dilutionneeded() for i in range(len(src))]
            watervols=[vol[i]-mmvols[i]-prefixvols[i]-suffixvols[i]-sampvols[i] for i in range(len(src))]

            print "water=",watervols,", mm=",mmvols,", prefix=",prefixvols,", suffix=",suffixvols,", samp=",sampvols
            self.e.multitransfer(watervols,decklayout.WATER,tgt,(False,False))		# Transfer water
            self.e.multitransfer(mmvols,mm,tgt,(False,False))	 # PCR master mix
            sprefixset=set(sprefix)
            ssuffixset=set(ssuffix)
            if len(sprefixset)<len(ssuffixset):
                # Distribute sprefix first
                for p in sprefixset:
                    self.e.multitransfer([prefixvols[i] for i in range(len(src)) if sprefix[i]==p],p,[tgt[i] for i in range(len(src)) if sprefix[i]==p],(False,False))
                # Then individually add ssuffix
                for i in range(len(src)):
                    self.e.transfer(suffixvols[i],ssuffix[i],tgt[i],(False,False))
            else:
                # Distribute ssuffix first
                for p in ssuffixset:
                    self.e.multitransfer([suffixvols[i] for i in range(len(src)) if ssuffix[i]==p],p,[tgt[i] for i in range(len(src)) if ssuffix[i]==p],(False,False))
                # Then individually add sprefix
                for i in range(len(src)):
                    self.e.transfer(prefixvols[i],sprefix[i],tgt[i],(False,False))
            # Now add templates
            for i in range(len(src)):
                self.e.transfer(sampvols[i],src[i],tgt[i],(False,False))
                
        else:
            primer=[prefix[i]+suffix[i] for i in range(len(prefix))]
            print "primer=",primer
            for up in set(primer):
                s="MPCR%s"%up
                if not reagents.isReagent(s):
                    reagents.add(name=s,conc=4/3.0,extraVol=30)
                self.e.stage('PCR%s'%up,[reagents.getsample("MPCR%s"%up)],[src[i] for i in range(len(src)) if primer[i]==up],[tgt[i] for i in range(len(tgt)) if primer[i]==up],[vol[i] for i in range(len(vol)) if primer[i]==up],destMix=False)
        pgm="PCR%d"%ncycles
        self.e.shakeSamples(tgt,returnPlate=False)
        #        worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@95,120 TEMP@95,30 TEMP@55,30 TEMP@72,25 GOTO@2,%d TEMP@72,180 TEMP@16,2'%(pgm,ncycles-1))
        worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@95,120 TEMP@95,10 TEMP@57,10 GOTO@2,%d TEMP@72,120 TEMP@25,2'%(pgm,ncycles-1))
        self.e.runpgm(pgm,4.80+1.55*ncycles,False,max(vol),hotlidmode="CONSTANT",hotlidtemp=100)
        return tgt

    def runPCRInPlace(self,prefix,src,vol,ncycles,suffix,annealtemp=57,save=None):
        [prefix,src,vol,suffix]=listify([prefix,src,vol,suffix])

        primer=[reagents.getsample("MPCR"+prefix[i]+suffix[i]) for i in range(len(prefix))]
        self.runRxInPlace(src,vol,primer,returnPlate=(save is not None))
        if save is not None:
            self.saveSamps(src=src,vol=5,dil=10,tgt=save,plate=decklayout.DILPLATE,dilutant=decklayout.SSDDIL)

        pgm="PCR%d"%ncycles
        #        worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@95,120 TEMP@95,30 TEMP@55,30 TEMP@72,25 GOTO@2,%d TEMP@72,180 TEMP@16,2'%(pgm,ncycles-1))
        worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@95,120 TEMP@95,10 TEMP@%f,10 GOTO@2,%d TEMP@72,120 TEMP@25,2'%(pgm,annealtemp,ncycles-1))
        self.e.runpgm(pgm,4.80+1.55*ncycles,False,max(vol),hotlidmode="CONSTANT",hotlidtemp=100)
    
    ########################
    # qPCR
    ########################
    def runQPCRDIL(self,src,vol,srcdil,tgt=None,dilPlate=False,pipMix=False,dilutant=decklayout.SSDDIL):
        [src,vol,srcdil]=listify([src,vol,srcdil])
        vol=[float(v) for v in vol]
        if tgt is None:
            if dilPlate:
                tgt=[Sample(diluteName(src[i].name,srcdil[i]),decklayout.DILPLATE) for i in range(len(src))]
            else:
                tgt=[Sample(diluteName(src[i].name,srcdil[i]),decklayout.SAMPLEPLATE) for i in range(len(src))]

        srcvol=[vol[i]/srcdil[i] for i in range(len(vol))]
        watervol=[vol[i]-srcvol[i] for i in range(len(vol))]
        if len(watervol) > 4 and sum(watervol)>800:
            print "Could optimize distribution of ",len(watervol)," moves of ",dilutant.name,": vol=[", ["%.1f"%w for w in watervol],"]"
        self.e.multitransfer(watervol,dilutant,tgt,(False,False))
        
        self.e.shakeSamples(src,returnPlate=True)
        for i in range(len(src)):
            tgt[i].conc=None		# Assume dilutant does not have a concentration of its own
            # Check if we can align the tips here
            if i<len(src)-3 and tgt[i].well+1==tgt[i+1].well and tgt[i].well+2==tgt[i+2].well and tgt[i].well+3==tgt[i+3].well and tgt[i].well%4==0 and self.e.cleanTips!=15:
                #print "Aligning tips"
                self.e.sanitize()
            self.e.transfer(srcvol[i],src[i],tgt[i],(not src[i].isMixed(),pipMix))
            if tgt[i].conc != None:
                tgt[i].conc.final=None	# Final conc are meaningless now
            
        return tgt
        
    def runQPCR(self,src,vol,srcdil,primers=["A","B"],nreplicates=1):
        ## QPCR setup
        worklist.comment("runQPCR: primers=%s, source=%s"%([p for p in primers],[s.name for s in src]))
        [src,vol,srcdil,nreplicates]=listify([src,vol,srcdil,nreplicates])
        self.e.shakeSamples(src,returnPlate=True)

        # Build a list of sets to be run
        torun=[]
        for repl in range(max(nreplicates)):
            for p in primers:
                for i in range(len(src)):
                    if nreplicates[i]<=repl:
                        continue
                    if repl==0:
                        sampname="%s.Q%s"%(src[i].name,p)
                    else:
                        sampname="%s.Q%s.%d"%(src[i].name,p,repl+1)
                    s=Sample(sampname,decklayout.QPCRPLATE)
                    torun=torun+[(src[i],s,p,vol[i])]

        # Fill the master mixes
        dil={}
        for p in primers:
            mname="MQ%s"%p
            if not reagents.isReagent(mname):
                reagents.add(name=mname,conc=15.0/9.0,extraVol=30)
            mq=reagents.getsample(mname)
            t=[a[1] for a in torun if a[2]==p]
            v=[a[3]/mq.conc.dilutionneeded() for a in torun if a[2]==p]
            self.e.multitransfer(v,mq,t,(False,False))
            dil[p]=1.0/(1-1/mq.conc.dilutionneeded())
            
        # Add the samples
        self.e.sanitize()		# In case we are aligned
        for a in torun:
            s=a[0]
            t=a[1]
            p=a[2]
            v=a[3]/dil[p]
            t.conc=None		# Concentration of master mix is irrelevant now
            self.e.transfer(v,s,t)
            
        return [a[1] for a in torun]

    def setup(self):
        'Setup for experiment -- run once.  Usually overridden by actual experiment'
        worklist.setOptimization(True)

    def pgm(self):
        'Actual robot code generation -- may be run multiple times to establish initial volumes.  Overridden by actual experiment'

    def run(self):
        parser=argparse.ArgumentParser(description="TRP")
        parser.add_argument('-v','--verbose',help='Enable verbose output',default=False,action="store_true")
        parser.add_argument('-D','--dewpoint',type=float,help='Dew point',default=10.0)
        args=parser.parse_args()
        
        print "Estimating evaporation for dew point of %.1f C"%args.dewpoint
        globals.dewpoint=args.dewpoint

        self.e=Experiment()
        self.e.setreagenttemp(args.dewpoint)
        self.e.sanitize(3,50)    # Heavy sanitize

        self.setup()
        if args.verbose:
            print '------ Preliminary run to set volume -----'
        else:
            sys.stdout=open(os.devnull,'w')
        self.pgm()
        self.reset()
        self.pgm()
        if args.verbose:
            globals.verbose=True
            print '------ Main run -----'
        else:
            sys.stdout=sys.__stdout__
        self.reset()
        self.pgm()
        self.finish()
Example #12
0
import os

from Experiment.sample import Sample
from Experiment.experiment import Experiment
from Experiment.experiment import Concentration

e = Experiment()

e.w.pyrun('PTC\\ptcsetpgm.py TEST TEMP@95,1  TEMP@25,1')
e.runpgm("TEST", 0, waitForCompletion=False)
e.waitpgm(sanitize=False)

e.savegem("platemovetest_orig.gem")

os.system(
    "grep -v 'Wash\|reagent tubes' platemovetest_orig.gem  > platemovetest.gem"
)
Example #13
0
class TRP(object):
    def __init__(self):
        'Create a new TRP run'

    def reset(self):
        'Reset this experiment so we can generate it again after adjusting the reagent initial volumes and total time'
        totalTime = clock.elapsed()
        clock.reset(totalTime)
        #print "After reset, elapsed=%d"%clock.elapsed()
        worklist.reset()
        self.e = Experiment()
        self.e.setreagenttemp(6.0)
        self.e.sanitize(3, 50)  # Heavy sanitize
        reagents.reset()
        Sample.clearall()
        decklayout.initWellKnownSamples()

    def addTemplates(self,
                     names,
                     stockconc,
                     finalconc=None,
                     units="nM",
                     plate=decklayout.EPPENDORFS):
        'Add templates as "reagents", return the list of them'
        if finalconc is None:
            print "WARNING: final concentration of template not specified, assuming 0.6x (should add to addTemplates() call"
            [names, stockconc] = listify([names, stockconc])
            finalconc = [0.6 * x for x in stockconc]
        else:
            [names, stockconc,
             finalconc] = listify([names, stockconc, finalconc])

        r = []
        for i in range(len(names)):
            r.append(
                reagents.add(names[i],
                             plate=plate,
                             conc=Concentration(stockconc[i], finalconc[i],
                                                units),
                             extraVol=30))
        return r

    def finish(self):
        self.e.lihahome()
        worklist.userprompt(
            "Process complete. Continue to turn off reagent cooler")
        self.e.setreagenttemp(None)

        #Sample.printallsamples("At completion")
        hasError = False
        for s in Sample.getAllOnPlate():
            if s.volume < 1.0 and s.conc is not None and not s.hasBeads:
                print "ERROR: Insufficient volume for ", s, " need at least ", 1.0 - s.volume, " ul additional"
                #hasError=True
            elif s.volume < 2.5 and s.conc is not None:
                print "WARNING: Low final volume for ", s
            elif s.volume > s.plate.maxVolume:
                print "ERROR: Excess final volume  (", s.volume, ") for ", s, ", maximum is ", s.plate.maxVolume
                hasError = True

        if hasError:
            print "NO OUTPUT DUE TO ERRORS"
            assert (False)

        print "Wells used:  samples: %d, dilutions: %d, qPCR: %d" % (
            Sample.numSamplesOnPlate(decklayout.SAMPLEPLATE),
            Sample.numSamplesOnPlate(decklayout.DILPLATE),
            Sample.numSamplesOnPlate(decklayout.QPCRPLATE))
        # Save worklist to a file
        #e.saveworklist("trp1.gwl")
        (scriptname, ext) = os.path.splitext(sys.argv[0])
        self.e.savegem(scriptname + ".gem")
        self.e.savesummary(scriptname + ".txt")
        Sample.savematlab(scriptname + ".m")

    ########################
    # Save samples to another well
    ########################
    def saveSamps(self,
                  src,
                  vol,
                  dil,
                  tgt=None,
                  dilutant=None,
                  plate=None,
                  mix=(True, False)):
        [src, vol, dil] = listify([src, vol, dil])
        if plate is None:
            plate = decklayout.REAGENTPLATE
        if tgt is None:
            tgt = [
                Sample(diluteName(src[i].name, dil[i]), plate)
                for i in range(len(src))
            ]

        if any([d != 1.0 for d in dil]):
            if dilutant is None:
                dilutant = decklayout.WATER
            self.e.multitransfer(
                [vol[i] * (dil[i] - 1) for i in range(len(vol))], dilutant,
                tgt, (False, False))

        self.e.shakeSamples(src, returnPlate=True)
        for i in range(len(src)):
            self.e.transfer(vol[i], src[i], tgt[i], mix)
            tgt[i].conc = Concentration(1.0 / dil[i])

        return tgt

    def distribute(self,
                   src,
                   dil,
                   vol,
                   wells,
                   tgt=None,
                   dilutant=None,
                   plate=decklayout.SAMPLEPLATE):
        if tgt is None:
            tgt = [
                Sample("%s.dist%d" % (src[0].name, j), plate)
                for j in range(wells)
            ]

        if dilutant is None:
            dilutant = decklayout.WATER
        self.e.multitransfer([vol * (dil - 1) for i in range(wells)], dilutant,
                             tgt)
        self.e.multitransfer([vol for i in range(wells)], src[0], tgt)
        return tgt

    ########################
    # Dilute samples in place
    ########################
    def diluteInPlace(self, tgt, dil=None, finalvol=None):
        # Dilute in place
        # e.g.: trp.diluteInPlace(tgt=rt1,dil=2)
        [tgt, dil, finalvol] = listify([tgt, dil, finalvol])
        dilutant = decklayout.WATER
        for i in range(len(tgt)):
            if finalvol[i] is not None and dil[i] is None:
                self.e.transfer(finalvol[i] - tgt[i].volume,
                                dilutant,
                                tgt[i],
                                mix=(False, False))
            elif finalvol[i] is None and dil[i] is not None:
                self.e.transfer(tgt[i].volume * (dil[i] - 1),
                                dilutant,
                                tgt[i],
                                mix=(False, False))
            else:
                print "diluteInPlace: cannot specify both dil and finalvol"
                assert (False)
        #print "after dilute, tgt[0]=",str(tgt[0]),",mixed=",tgt[0].isMixed()
        return tgt  #  The name of the samples are unchanged -- the predilution names

    ########################
    # Run a reaction in place
    ########################
    def runRxInPlace(self, src, vol, master, returnPlate=True, finalx=1.0):
        'Run reaction on beads in given total volume'
        [vol, src, master] = listify([vol, src, master])
        mastervol = [
            vol[i] * finalx / master[i].conc.dilutionneeded()
            for i in range(len(vol))
        ]
        watervol = [
            vol[i] - src[i].volume - mastervol[i] for i in range(len(vol))
        ]
        if any([w < -0.01 for w in watervol]):
            print "runRxInPlace: negative amount of water needed: ", w
            assert (False)
        for i in range(len(src)):
            if watervol[i] > 0:
                self.e.transfer(watervol[i], decklayout.WATER, src[i],
                                (False, False))
        for i in range(len(src)):
            self.e.transfer(mastervol[i], master[i], src[i],
                            (True, src[i].hasBeads))
        self.e.shakeSamples(src, returnPlate=returnPlate)

    ########################
    # T7 - Transcription
    ########################
    def runT7Setup(self, theo, src, vol, srcdil, tgt=None, rlist=["MT7"]):
        [theo, src, tgt, srcdil] = listify([theo, src, tgt, srcdil])
        for i in range(len(src)):
            if tgt[i] is None:
                if theo[i]:
                    tgt[i] = Sample("%s.T+" % src[i].name,
                                    decklayout.SAMPLEPLATE)
                else:
                    tgt[i] = Sample("%s.T-" % src[i].name,
                                    decklayout.SAMPLEPLATE)

        worklist.comment("runT7: source=%s" % [str(s) for s in src])

        rvols = [reagents.getsample(x).conc.volneeded(vol) for x in rlist]
        rtotal = sum(rvols)
        sourcevols = [vol * 1.0 / s for s in srcdil]
        if any(theo):
            theovols = [
                (vol * 1.0 /
                 reagents.getsample("Theo").conc.dilutionneeded() if t else 0)
                for t in theo
            ]
            watervols = [
                vol - theovols[i] - sourcevols[i] - rtotal
                for i in range(len(src))
            ]
        else:
            watervols = [vol - sourcevols[i] - rtotal for i in range(len(src))]

        if any([w < -1e-10 for w in watervols]):
            print "runT7Setup: Negative amount of water required: ", watervols
            assert False
        if sum(watervols) > 0.01:
            self.e.multitransfer(watervols, decklayout.WATER, tgt)
        for ir in range(len(rlist)):
            self.e.multitransfer([rvols[ir] for s in tgt],
                                 reagents.getsample(rlist[ir]), tgt)
        if any(theo):
            self.e.multitransfer(
                [tv for tv in theovols if tv > 0.01],
                reagents.getsample("Theo"),
                [tgt[i] for i in range(len(theovols)) if theovols[i] > 0],
                ignoreContents=True)
        for i in range(len(src)):
            self.e.transfer(sourcevols[i], src[i], tgt[i])
        self.e.shakeSamples(tgt, returnPlate=True)
        for t in tgt:
            t.ingredients['BIND'] = 1e-20 * sum(t.ingredients.values())
        return tgt

    def runT7Pgm(self, vol, dur):
        if dur < 100:
            pgm = "TRP37-%d" % dur
        else:
            pgm = "T37-%d" % dur
        worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@37,%d TEMP@25,2' %
                       (pgm, dur * 60))
        self.e.runpgm(pgm, dur, False, vol)

    def runT7Stop(self, theo, tgt, stopmaster=None, srcdil=2):
        [theo, tgt, stopmaster,
         srcdil] = listify([theo, tgt, stopmaster, srcdil])
        if stopmaster is None:
            stopmaster = ["MStpS_NT" if t == 0 else "MStpS_WT" for t in theo]

        # Adjust source dilution
        for i in range(len(tgt)):
            tgt[i].conc = Concentration(srcdil[i], 1)

        ## Stop
        sstopmaster = [reagents.getsample(s) for s in stopmaster]
        for i in range(len(tgt)):
            stopvol = tgt[i].volume / (sstopmaster[i].conc.dilutionneeded() -
                                       1)
            finalvol = tgt[i].volume + stopvol
            self.e.transfer(finalvol - tgt[i].volume, sstopmaster[i], tgt[i])

        self.e.shakeSamples(tgt, returnPlate=True)

        return tgt

    def runT7(self, theo, src, vol, srcdil, tgt=None, dur=15, stopmaster=None):
        [theo, src, tgt, srcdil,
         stopmaster] = listify([theo, src, tgt, srcdil, stopmaster])
        tgt = self.runT7Setup(theo, src, vol, srcdil, tgt)
        self.runT7Pgm(vol, dur)
        tgt = self.runT7Stop(theo, tgt, stopmaster)
        return tgt

    ########################
    # Beads
    ########################
    def bindBeads(self,
                  src,
                  beads=None,
                  beadConc=None,
                  bbuffer=None,
                  incTime=60,
                  addBuffer=False):
        if beads is None:
            beads = reagents.getsample("Dynabeads")
        if bbuffer is None:
            bbuffer = reagents.getsample("BeadBuffer")

        [src, beads, bbuffer,
         beadConc] = listify([src, beads, bbuffer, beadConc])

        for s in src:
            if s.plate != decklayout.SAMPLEPLATE:
                print "runBeadCleanup: src ", s, " is not in sample plate."
                assert (0)
            s.conc = None  # Can't track concentration of beads

        self.e.moveplate(src[0].plate,
                         "Home")  # Make sure we do this off the magnet

        # Calculate volumes needed
        beadConc = [
            beads[i].conc.final if beadConc[i] is None else beadConc[i]
            for i in range(len(beads))
        ]
        beadDil = beads[i].conc.stock / beadConc[i]
        if addBuffer:
            totalvol = [
                s.volume /
                (1 - 1.0 / beadDil - 1.0 / bbuffer[i].conc.dilutionneeded())
                for s in src
            ]
            buffervol = [
                totalvol[i] / bbuffer[i].conc.dilutionneeded()
                for i in range(len(src))
            ]
            # Add binding buffer to bring to 1x (beads will already be in 1x, so don't need to provide for them)
            for i in range(len(src)):
                self.e.transfer(buffervol[i], bbuffer[i], src[i])
        else:
            buffervol = [0.0 for i in range(len(src))]
            totalvol = [s.volume / (1 - 1.0 / beadDil) for s in src]

        beadvol = [t / beadDil for t in totalvol]

        # Transfer the beads
        for i in range(len(src)):
            self.e.transfer(
                beadvol[i], beads[i], src[i], (True, True)
            )  # Mix beads after (before mixing handled automatically by sample.py)

        self.e.shake(src[0].plate, dur=incTime, returnPlate=False)

    def sepWait(self, src, sepTime=None):
        if sepTime is None:
            maxvol = max([s.volume for s in src])
            if maxvol > 50:
                sepTime = 50
            else:
                sepTime = 30
        self.e.pause(sepTime)  # Wait for separation

    def beadWash(self,
                 src,
                 washTgt=None,
                 sepTime=None,
                 residualVolume=10,
                 keepWash=False,
                 numWashes=2,
                 wash=None,
                 washVol=50,
                 keepFinal=False,
                 finalTgt=None,
                 keepVol=4.2,
                 keepDil=5):
        # Perform washes
        # If keepWash is true, retain all washes (combined)
        # If keepFinal is true, take a sample of the final wash (diluted by keepDil)
        if wash is None:
            wash = decklayout.WATER
        [src, wash] = listify([src, wash])
        # Do all washes while on magnet
        assert (len(set([s.plate for s in src])) == 1)  # All on same plate
        if keepWash:
            if washTgt is None:
                washTgt = []
                for i in range(len(src)):
                    if s[i].volume - residualVolume + numWashes * (
                            washVol - residualVolume
                    ) > decklayout.DILPLATE.maxVolume - 20:
                        print "Saving %.1f ul of wash in eppendorfs" % (
                            numWashes * washVol)
                        washTgt.append(
                            Sample("%s.Wash" % src[i].name,
                                   decklayout.EPPENDORFS))
                    else:
                        washTgt.append(
                            Sample("%s.Wash" % src[i].name,
                                   decklayout.DILPLATE))

        if keepFinal:
            if finalTgt is None:
                finalTgt = []
                for i in range(len(src)):
                    finalTgt.append(
                        Sample("%s.Final" % src[i].name, decklayout.DILPLATE))

        if any([s.volume > residualVolume for s in src]):
            # Separate and remove supernatant
            self.e.moveplate(src[0].plate, "Magnet")  # Move to magnet
            self.sepWait(src, sepTime)

            # Remove the supernatant
            for i in range(len(src)):
                if src[i].volume > residualVolume:
                    amt = src[i].amountToRemove(residualVolume)
                    if keepWash:
                        self.e.transfer(amt, src[i],
                                        washTgt[i])  # Keep supernatants
                        washTgt[i].conc = None  # Allow it to be reused
                    else:
                        self.e.dispose(amt, src[i])  # Discard supernatant

        # Wash

        for washnum in range(numWashes):
            self.e.moveplate(src[0].plate, "Home")
            if keepFinal and washnum == numWashes - 1:
                'Retain sample of final'
                for i in range(len(src)):
                    src[i].conc = None
                    self.e.transfer(washVol - src[i].volume,
                                    wash[i],
                                    src[i],
                                    mix=(False, True))  # Add wash
                self.e.shake(src[0].plate, returnPlate=True)
                self.saveSamps(src=src,
                               tgt=finalTgt,
                               vol=keepVol,
                               dil=keepDil,
                               plate=decklayout.DILPLATE)
            else:
                for i in range(len(src)):
                    src[i].conc = None
                    self.e.transfer(
                        washVol - src[i].volume,
                        wash[i],
                        src[i],
                        mix=(False, False)
                    )  # Add wash, no need to pipette mix since some heterogenity won't hurt here
                self.e.shake(src[0].plate, returnPlate=False)

            self.e.moveplate(src[0].plate, "Magnet")  # Move to magnet

            self.sepWait(src, sepTime)

            for i in range(len(src)):
                amt = src[i].amountToRemove(residualVolume)
                if keepWash:
                    self.e.transfer(amt, src[i], washTgt[i])  # Remove wash
                    washTgt[i].conc = None  # Allow it to be reused
                else:
                    self.e.dispose(amt, src[i])  # Remove wash

        self.e.moveplate(src[0].plate, "Home")

        # Should only be residualVolume left with beads now
        result = []
        if keepWash:
            result = result + washTgt
        if keepFinal:
            result = result + finalTgt

        return result

    def beadAddElutant(self,
                       src,
                       elutant=None,
                       elutionVol=30,
                       eluteTime=60,
                       returnPlate=True,
                       temp=None):
        if elutant is None:
            elutant = decklayout.WATER
        [src, elutionVol, elutant] = listify([src, elutionVol, elutant])
        for i in range(len(src)):
            if elutionVol[i] < 30:
                print "WARNING: elution from beads with %.1f ul < minimum of 30ul" % elutionVol[
                    i]
                print "  src=", src[i]
            self.e.transfer(elutionVol[i] - src[i].volume, elutant[i], src[i],
                            (False, True))
        if temp is None:
            self.e.shake(src[0].plate, dur=eluteTime, returnPlate=returnPlate)
        else:
            self.e.shake(src[0].plate, dur=30, returnPlate=False)
            worklist.pyrun('PTC\\ptcsetpgm.py elute TEMP@%d,%d TEMP@25,2' %
                           (temp, eluteTime))
            self.e.runpgm("elute", eluteTime / 60, False, elutionVol[0])
            if returnPlate:
                self.e.moveplate(src[0].plate, "Home")

    def beadSupernatant(self,
                        src,
                        tgt=None,
                        sepTime=None,
                        residualVolume=10,
                        plate=None):
        if plate is None:
            plate = decklayout.SAMPLEPLATE
        if tgt is None:
            tgt = []
            for i in range(len(src)):
                tgt.append(Sample("%s.SN" % src[i].name, plate))
        [src, tgt] = listify([src, tgt])

        if any([s.plate != src[0].plate for s in src]):
            print "beadSupernatant: Attempt to magsep on multiple plates at the same time"
            assert False

        self.e.moveplate(src[0].plate, "Magnet")  # Move to magnet
        self.sepWait(src, sepTime)

        for i in range(len(src)):
            self.e.transfer(src[i].amountToRemove(residualVolume), src[i],
                            tgt[i],
                            (False, False))  # Transfer elution to new tube

        self.e.moveplate(src[0].plate, "Home")
        return tgt

    def beadCombine(self,
                    src,
                    residualVolume=10,
                    suspendVolume=150,
                    sepTime=None):
        'Combine everything in the src wells into a the first well; assumes that there are enough beads in that well for all the combination'
        tgt = src[0]
        for s in src[1:]:
            # Combine s with tgt
            if tgt.volume > residualVolume:
                self.e.moveplate(tgt.plate, "Magnet")  # Move to magnet
                self.sepWait([tgt], sepTime)
                self.e.dispose(tgt.amountToRemove(residualVolume), tgt)
            self.e.moveplate(tgt.plate, "Home")
            if s.volume < suspendVolume:
                self.e.transfer(suspendVolume - s.volume, decklayout.WATER, s,
                                (False, False))
            vol = s.volume - residualVolume - 1
            s.conc = None
            self.e.transfer(vol, s, tgt, mix=(True, True))

        self.e.moveplate(tgt.plate, "Home")
        return src[0:1]

    ########################
    # RT - Reverse Transcription
    ########################
    def runRT(self, src, vol, srcdil, tgt=None, dur=20, heatInactivate=False):
        result = self.runRTSetup(src, vol, srcdil, tgt)
        self.runRTPgm(dur, heatInactivate=heatInactivate)
        return result

    def runRTInPlace(self, src, vol, dur=20, heatInactivate=False):
        'Run RT on beads in given volume'

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc = None

        self.runRxInPlace(src,
                          vol,
                          reagents.getsample("MPosRT"),
                          returnPlate=False)
        self.runRTPgm(dur, heatInactivate=heatInactivate)

    def runRTSetup(self, src, vol, srcdil, tgt=None, rtmaster=None):
        if rtmaster is None:
            rtmaster = reagents.getsample("MPosRT")
        if tgt is None:
            tgt = [
                Sample(s.name + ".RT+", decklayout.SAMPLEPLATE) for s in src
            ]

        [src, tgt, vol, srcdil] = listify([src, tgt, vol, srcdil])

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc = Concentration(srcdil[i], 1)

        self.e.stage('RTPos', [rtmaster], [src[i] for i in range(len(src))],
                     [tgt[i] for i in range(len(tgt))],
                     [vol[i] for i in range(len(vol))],
                     destMix=False)
        #self.e.shakeSamples(tgt,returnPlate=True)
        return tgt

    def runRTPgm(self, dur=20, heatInactivate=False):
        if heatInactivate:
            hidur = 2
            pgm = "RT-%d" % dur
            worklist.pyrun(
                'PTC\\ptcsetpgm.py %s TEMP@37,%d TEMP@95,%d TEMP@25,2 RATE 0.5'
                % (pgm, dur * 60, hidur * 60))
            self.e.runpgm(
                pgm, dur + hidur + 2.5, False, 100
            )  # Volume doesn't matter since it's just an incubation, use 100ul
        else:
            if dur < 100:
                pgm = "TRP37-%d" % dur
            else:
                pgm = "T37-%d" % dur
            worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@37,%d TEMP@25,2' %
                           (pgm, dur * 60))
            self.e.runpgm(
                pgm, dur, False, 100
            )  # Volume doesn't matter since it's just an incubation, use 100ul

    ########################
    # Lig - Ligation
    ########################
    def runLig(self,
               prefix=None,
               src=None,
               vol=None,
               srcdil=None,
               tgt=None,
               master=None,
               anneal=True,
               ligtemp=25):
        if master is None:
            master = [
                reagents.getsample("MLigAN7")
                if p == 'A' else reagents.getsample("MLigBN7") for p in prefix
            ]

        #Extension
        [src, tgt, vol, srcdil,
         master] = listify([src, tgt, vol, srcdil, master])
        if tgt is None:
            tgt = [
                Sample("%s.%s" % (src[i].name, master[i].name),
                       decklayout.SAMPLEPLATE) for i in range(len(src))
            ]

        # Need to check since an unused ligation master mix will not have a concentration
        minsrcdil = 1 / (1 - 1 / master[0].conc.dilutionneeded() - 1 /
                         reagents.getsample("MLigase").conc.dilutionneeded())
        for i in srcdil:
            if i < minsrcdil:
                print "runLig: srcdil=%.2f, but must be at least %.2f based on concentrations of master mixes" % (
                    i, minsrcdil)
                assert (False)

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc = Concentration(srcdil[i], 1)

        i = 0
        while i < len(tgt):
            lasti = i + 1
            while lasti < len(tgt) and master[i] == master[lasti]:
                lasti = lasti + 1
            self.e.stage('LigAnneal', [master[i]],
                         src[i:lasti],
                         tgt[i:lasti], [vol[j] / 1.5 for j in range(i, lasti)],
                         1.5,
                         destMix=False)
            i = lasti

        if anneal:
            self.e.shakeSamples(tgt, returnPlate=False)
            self.e.runpgm("TRPANN",
                          5,
                          False,
                          max(vol),
                          hotlidmode="CONSTANT",
                          hotlidtemp=100)
        self.e.stage('Ligation', [reagents.getsample("MLigase")], [],
                     tgt,
                     vol,
                     destMix=False)
        self.e.shakeSamples(tgt, returnPlate=False)
        self.runLigPgm(max(vol), ligtemp)
        return tgt

    def runLigPgm(self, vol, ligtemp, inactivate=True, inacttemp=65):
        if inactivate:
            pgm = "LIG15-%.0f" % ligtemp
            worklist.pyrun(
                'PTC\\ptcsetpgm.py %s TEMP@%.0f,900 TEMP@%.0f,600 TEMP@25,30' %
                (pgm, ligtemp, inacttemp))
            self.e.runpgm(pgm,
                          27,
                          False,
                          vol,
                          hotlidmode="TRACKING",
                          hotlidtemp=10)
        elif ligtemp == 25:
            worklist.comment('Ligation at room temp')
            self.e.pause(15 * 60)
        else:
            pgm = "TRP%.0f-15" % ligtemp
            worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@%.0f,900 TEMP@25,30' %
                           (pgm, ligtemp))
            self.e.runpgm(pgm,
                          17,
                          False,
                          vol,
                          hotlidmode="TRACKING",
                          hotlidtemp=10)

    def runLigInPlace(self, src, vol, ligmaster, anneal=True, ligtemp=25):
        'Run ligation on beads'
        [vol, src] = listify([vol, src])
        annealvol = [
            v * (1 - 1 / reagents.getsample("MLigase").conc.dilutionneeded())
            for v in vol
        ]

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc = None

        self.runRxInPlace(src,
                          annealvol,
                          reagents.getsample(ligmaster),
                          returnPlate=not anneal,
                          finalx=1.5)
        if anneal:
            self.e.runpgm("TRPANN",
                          5,
                          False,
                          max([s.volume for s in src]),
                          hotlidmode="CONSTANT",
                          hotlidtemp=100)

        ## Add ligase
        self.runRxInPlace(src,
                          vol,
                          reagents.getsample("MLigase"),
                          returnPlate=False)
        self.runLigPgm(
            max(vol), ligtemp, inactivate=False
        )  # Do not heat inactivate since it may denature the beads

    ########################
    # Incubation - run a single temp incubation followed by inactivation
    ########################
    def runIncubation(self,
                      src=None,
                      vol=None,
                      srcdil=None,
                      tgt=None,
                      enzymes=None,
                      incTemp=37,
                      incTime=15,
                      hiTemp=None,
                      hiTime=0,
                      inPlace=False):
        if len(enzymes) != 1:
            print "ERROR: runIncubation only supports a single master mix"
            assert False
        if inPlace:
            if tgt is not None:
                print "ERROR: tgt specified for in-place incubation"
                assert False
        elif tgt is None:
            tgt = [
                Sample("%s.%s" % (src[i].name, enzymes[0].name),
                       decklayout.SAMPLEPLATE) for i in range(len(src))
            ]

        if srcdil == None:
            # Minimum dilution (no water)
            srcdil = 1 / (1 -
                          sum([1 / e.conc.dilutionneeded() for e in enzymes]))

        if vol is None and inPlace:
            vol = [s.volume * srcdil for s in src]

        [src, tgt, vol, srcdil] = listify([src, tgt, vol, srcdil])

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc = Concentration(srcdil[i], 1)

        if inPlace:
            self.runRxInPlace(src, vol, enzymes[0], returnPlate=False)
            tgt = src
        else:
            self.e.stage('User', enzymes, src, tgt, vol, destMix=False)
            self.e.shakeSamples(tgt, returnPlate=False)

        if hiTemp is None:
            worklist.pyrun('PTC\\ptcsetpgm.py INC TEMP@%.0f,%.0f TEMP@25,30' %
                           (incTemp, incTime * 60))
        else:
            assert (hiTime > 0)
            worklist.pyrun(
                'PTC\\ptcsetpgm.py INC TEMP@%.0f,%.0f TEMP@%.0f,%.0f TEMP@25,30'
                % (incTemp, incTime * 60, hiTemp, hiTime * 60))
        self.e.runpgm("INC",
                      incTime + hiTime + 2,
                      False,
                      max(vol),
                      hotlidmode="TRACKING",
                      hotlidtemp=10)
        return tgt

    ########################
    # USER - USER enzyme digestion
    ########################
    def runUser(self,
                src=None,
                vol=None,
                srcdil=None,
                tgt=None,
                incTime=15,
                inPlace=False):
        return self.runIncubation(src=src,
                                  vol=vol,
                                  srcdil=srcdil,
                                  tgt=tgt,
                                  incTemp=37,
                                  incTime=incTime,
                                  enzymes=[reagents.getsample("MUser")],
                                  inPlace=inPlace)

    ########################
    # Klenow extension
    ########################
    def runKlenow(self,
                  src=None,
                  vol=None,
                  srcdil=None,
                  tgt=None,
                  incTime=15,
                  hiTime=20,
                  inPlace=False):
        assert (inPlace or vol is not None)
        return self.runIncubation(src=src,
                                  vol=vol,
                                  srcdil=srcdil,
                                  tgt=tgt,
                                  incTemp=37,
                                  incTime=incTime,
                                  hiTemp=75,
                                  hiTime=hiTime,
                                  enzymes=[reagents.getsample("MKlenow")],
                                  inPlace=inPlace)

    ########################
    # DNase digestion
    ########################
    def runDNase(self,
                 src=None,
                 vol=None,
                 srcdil=None,
                 tgt=None,
                 incTime=15,
                 hiTime=10,
                 inPlace=False):
        return self.runIncubation(src=src,
                                  vol=vol,
                                  srcdil=srcdil,
                                  tgt=tgt,
                                  incTemp=37,
                                  incTime=incTime,
                                  hiTemp=75,
                                  hiTime=hiTime,
                                  enzymes=[reagents.getsample("MDNase")],
                                  inPlace=inPlace)

    ########################
    # PCR
    ########################
    def runPCR(self,
               prefix,
               src,
               vol,
               srcdil,
               tgt=None,
               ncycles=20,
               suffix='S',
               sepPrimers=True,
               primerDil=4):
        ## PCR
        [prefix, src, tgt, vol, srcdil,
         suffix] = listify([prefix, src, tgt, vol, srcdil, suffix])
        for i in range(len(tgt)):
            if tgt[i] is None:
                tgt[i] = Sample(
                    "%s.P%s%s" % (src[i].name, prefix[i], suffix[i]),
                    src[i].plate)

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc = Concentration(srcdil[i], 1)

        if sepPrimers:
            sampvols = [vol[i] / srcdil[i] for i in range(len(src))]
            mm = reagents.getsample("MPCR")
            mmvols = [
                vol[i] / mm.conc.dilutionneeded() for i in range(len(src))
            ]
            for s in prefix + suffix:
                if not reagents.isReagent(s):
                    reagents.add(name=s, conc=primerDil, extraVol=30)

            sprefix = [reagents.getsample(p) for p in prefix]
            ssuffix = [reagents.getsample(p) for p in suffix]

            prefixvols = [
                vol[i] / sprefix[i].conc.dilutionneeded()
                for i in range(len(src))
            ]
            suffixvols = [
                vol[i] / ssuffix[i].conc.dilutionneeded()
                for i in range(len(src))
            ]
            watervols = [
                vol[i] - mmvols[i] - prefixvols[i] - suffixvols[i] -
                sampvols[i] for i in range(len(src))
            ]

            print "water=", watervols, ", mm=", mmvols, ", prefix=", prefixvols, ", suffix=", suffixvols, ", samp=", sampvols
            self.e.multitransfer(watervols, decklayout.WATER, tgt,
                                 (False, False))  # Transfer water
            self.e.multitransfer(mmvols, mm, tgt,
                                 (False, False))  # PCR master mix
            sprefixset = set(sprefix)
            ssuffixset = set(ssuffix)
            if len(sprefixset) < len(ssuffixset):
                # Distribute sprefix first
                for p in sprefixset:
                    self.e.multitransfer([
                        prefixvols[i]
                        for i in range(len(src)) if sprefix[i] == p
                    ], p, [tgt[i] for i in range(len(src)) if sprefix[i] == p],
                                         (False, False))
                # Then individually add ssuffix
                for i in range(len(src)):
                    self.e.transfer(suffixvols[i], ssuffix[i], tgt[i],
                                    (False, False))
            else:
                # Distribute ssuffix first
                for p in ssuffixset:
                    self.e.multitransfer([
                        suffixvols[i]
                        for i in range(len(src)) if ssuffix[i] == p
                    ], p, [tgt[i] for i in range(len(src)) if ssuffix[i] == p],
                                         (False, False))
                # Then individually add sprefix
                for i in range(len(src)):
                    self.e.transfer(prefixvols[i], sprefix[i], tgt[i],
                                    (False, False))
            # Now add templates
            for i in range(len(src)):
                self.e.transfer(sampvols[i], src[i], tgt[i], (False, False))

        else:
            primer = [prefix[i] + suffix[i] for i in range(len(prefix))]
            print "primer=", primer
            for up in set(primer):
                s = "MPCR%s" % up
                if not reagents.isReagent(s):
                    reagents.add(name=s, conc=4 / 3.0, extraVol=30)
                self.e.stage(
                    'PCR%s' % up, [reagents.getsample("MPCR%s" % up)],
                    [src[i] for i in range(len(src)) if primer[i] == up],
                    [tgt[i] for i in range(len(tgt)) if primer[i] == up],
                    [vol[i] for i in range(len(vol)) if primer[i] == up],
                    destMix=False)
        pgm = "PCR%d" % ncycles
        self.e.shakeSamples(tgt, returnPlate=False)
        #        worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@95,120 TEMP@95,30 TEMP@55,30 TEMP@72,25 GOTO@2,%d TEMP@72,180 TEMP@16,2'%(pgm,ncycles-1))
        worklist.pyrun(
            'PTC\\ptcsetpgm.py %s TEMP@95,120 TEMP@95,10 TEMP@57,10 GOTO@2,%d TEMP@72,120 TEMP@25,2'
            % (pgm, ncycles - 1))
        self.e.runpgm(pgm,
                      4.80 + 1.55 * ncycles,
                      False,
                      max(vol),
                      hotlidmode="CONSTANT",
                      hotlidtemp=100)
        return tgt

    def runPCRInPlace(self,
                      prefix,
                      src,
                      vol,
                      ncycles,
                      suffix,
                      annealtemp=57,
                      save=None):
        [prefix, src, vol, suffix] = listify([prefix, src, vol, suffix])

        primer = [
            reagents.getsample("MPCR" + prefix[i] + suffix[i])
            for i in range(len(prefix))
        ]
        self.runRxInPlace(src, vol, primer, returnPlate=(save is not None))
        if save is not None:
            self.saveSamps(src=src,
                           vol=5,
                           dil=10,
                           tgt=save,
                           plate=decklayout.DILPLATE,
                           dilutant=decklayout.SSDDIL)

        pgm = "PCR%d" % ncycles
        #        worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@95,120 TEMP@95,30 TEMP@55,30 TEMP@72,25 GOTO@2,%d TEMP@72,180 TEMP@16,2'%(pgm,ncycles-1))
        worklist.pyrun(
            'PTC\\ptcsetpgm.py %s TEMP@95,120 TEMP@95,10 TEMP@%f,10 GOTO@2,%d TEMP@72,120 TEMP@25,2'
            % (pgm, annealtemp, ncycles - 1))
        self.e.runpgm(pgm,
                      4.80 + 1.55 * ncycles,
                      False,
                      max(vol),
                      hotlidmode="CONSTANT",
                      hotlidtemp=100)

    ########################
    # qPCR
    ########################
    def runQPCRDIL(self,
                   src,
                   vol,
                   srcdil,
                   tgt=None,
                   dilPlate=False,
                   pipMix=False,
                   dilutant=decklayout.SSDDIL):
        [src, vol, srcdil] = listify([src, vol, srcdil])
        vol = [float(v) for v in vol]
        if tgt is None:
            if dilPlate:
                tgt = [
                    Sample(diluteName(src[i].name, srcdil[i]),
                           decklayout.DILPLATE) for i in range(len(src))
                ]
            else:
                tgt = [
                    Sample(diluteName(src[i].name, srcdil[i]),
                           decklayout.SAMPLEPLATE) for i in range(len(src))
                ]

        srcvol = [vol[i] / srcdil[i] for i in range(len(vol))]
        watervol = [vol[i] - srcvol[i] for i in range(len(vol))]
        if len(watervol) > 4 and sum(watervol) > 800:
            print "Could optimize distribution of ", len(
                watervol), " moves of ", dilutant.name, ": vol=[", [
                    "%.1f" % w for w in watervol
                ], "]"
        self.e.multitransfer(watervol, dilutant, tgt, (False, False))

        self.e.shakeSamples(src, returnPlate=True)
        for i in range(len(src)):
            tgt[i].conc = None  # Assume dilutant does not have a concentration of its own
            # Check if we can align the tips here
            if i < len(src) - 3 and tgt[i].well + 1 == tgt[
                    i + 1].well and tgt[i].well + 2 == tgt[
                        i +
                        2].well and tgt[i].well + 3 == tgt[i + 3].well and tgt[
                            i].well % 4 == 0 and self.e.cleanTips != 15:
                #print "Aligning tips"
                self.e.sanitize()
            self.e.transfer(srcvol[i], src[i], tgt[i],
                            (not src[i].isMixed(), pipMix))
            if tgt[i].conc != None:
                tgt[i].conc.final = None  # Final conc are meaningless now

        return tgt

    def runQPCR(self, src, vol, srcdil, primers=["A", "B"], nreplicates=1):
        ## QPCR setup
        worklist.comment("runQPCR: primers=%s, source=%s" %
                         ([p for p in primers], [s.name for s in src]))
        [src, vol, srcdil,
         nreplicates] = listify([src, vol, srcdil, nreplicates])
        self.e.shakeSamples(src, returnPlate=True)

        # Build a list of sets to be run
        torun = []
        for repl in range(max(nreplicates)):
            for p in primers:
                for i in range(len(src)):
                    if nreplicates[i] <= repl:
                        continue
                    if repl == 0:
                        sampname = "%s.Q%s" % (src[i].name, p)
                    else:
                        sampname = "%s.Q%s.%d" % (src[i].name, p, repl + 1)
                    s = Sample(sampname, decklayout.QPCRPLATE)
                    torun = torun + [(src[i], s, p, vol[i])]

        # Fill the master mixes
        dil = {}
        for p in primers:
            mname = "MQ%s" % p
            if not reagents.isReagent(mname):
                reagents.add(name=mname, conc=15.0 / 9.0, extraVol=30)
            mq = reagents.getsample(mname)
            t = [a[1] for a in torun if a[2] == p]
            v = [a[3] / mq.conc.dilutionneeded() for a in torun if a[2] == p]
            self.e.multitransfer(v, mq, t, (False, False))
            dil[p] = 1.0 / (1 - 1 / mq.conc.dilutionneeded())

        # Add the samples
        self.e.sanitize()  # In case we are aligned
        for a in torun:
            s = a[0]
            t = a[1]
            p = a[2]
            v = a[3] / dil[p]
            t.conc = None  # Concentration of master mix is irrelevant now
            self.e.transfer(v, s, t)

        return [a[1] for a in torun]

    def setup(self):
        'Setup for experiment -- run once.  Usually overridden by actual experiment'
        worklist.setOptimization(True)

    def pgm(self):
        'Actual robot code generation -- may be run multiple times to establish initial volumes.  Overridden by actual experiment'

    def run(self):
        parser = argparse.ArgumentParser(description="TRP")
        parser.add_argument('-v',
                            '--verbose',
                            help='Enable verbose output',
                            default=False,
                            action="store_true")
        parser.add_argument('-D',
                            '--dewpoint',
                            type=float,
                            help='Dew point',
                            default=10.0)
        args = parser.parse_args()

        print "Estimating evaporation for dew point of %.1f C" % args.dewpoint
        globals.dewpoint = args.dewpoint

        self.e = Experiment()
        self.e.setreagenttemp(args.dewpoint)
        self.e.sanitize(3, 50)  # Heavy sanitize

        self.setup()
        if args.verbose:
            print '------ Preliminary run to set volume -----'
        else:
            sys.stdout = open(os.devnull, 'w')
        self.pgm()
        self.reset()
        self.pgm()
        if args.verbose:
            globals.verbose = True
            print '------ Main run -----'
        else:
            sys.stdout = sys.__stdout__
        self.reset()
        self.pgm()
        self.finish()
Example #14
0
class MSetup(object):
    TGTINVOL = 4

    def __init__(self,
                 trp,
                 vol=15,
                 maxdil=16,
                 mindilvol=90,
                 maxdilvol=100,
                 debug=False):
        'Create a new QPCR setup structure'
        self.volume = vol
        self.samples = []
        self.needDil = []
        self.primers = []
        self.nreplicates = []
        self.dilProds = []
        self.reuse = [
        ]  # Index of prior dilution that can be used as input to this one; otherwise None
        self.stages = []
        self.MAXDIL = maxdil
        self.MINDILVOL = mindilvol
        self.MAXDILVOL = maxdilvol
        self.trp = trp
        self.debug = debug
        self.dilutant = decklayout.SSDDIL
        self.jobq = JobQueue()
        self.e = Experiment()

    def addSamples(self,
                   src,
                   needDil,
                   primers,
                   nreplicates=1,
                   names=None,
                   saveVol=None,
                   saveDil=None,
                   save=True):
        'Add sample(s) to list of qPCRs to do'
        #print "addSamples(%s)"%src
        if not isinstance(src, list):
            src = [src]
        if save:
            # saveVol is total amount (after dilution) to be immediately saved
            if saveDil is None:
                saveDil = min(needDil, self.MAXDIL)
                if needDil / saveDil > 1 and needDil / saveDil < 2:
                    saveDil = math.sqrt(needDil)
            elif saveDil > needDil:
                logging.warning("addSamples: saveDil=", saveDil,
                                ", but needDil is only ", needDil)
                saveDil = needDil

            if saveVol is None:
                saveVol = max(self.MINDILVOL * 1.0 / saveDil, self.TGTINVOL)

            if names is None:
                tgt = [
                    Sample(diluteName(src[i].name, saveDil),
                           decklayout.DILPLATE) for i in range(len(src))
                ]
            else:
                tgt = [
                    Sample(diluteName(names[i], saveDil), decklayout.DILPLATE)
                    for i in range(len(src))
                ]
            sv = tgt

            for i in range(len(sv)):
                #print "Save ",src[i]
                svtmp = self.trp.runQPCRDIL(src=[src[i]],
                                            vol=saveVol * saveDil,
                                            srcdil=saveDil,
                                            tgt=[tgt[i]],
                                            dilPlate=True,
                                            dilutant=self.dilutant)
                sv[i] = svtmp[0]
        else:
            saveDil = 1
            sv = src

        needDil = needDil / saveDil
        nstages = int(math.ceil(math.log(needDil) / math.log(self.MAXDIL)))
        ndil = len(src) * (nstages + (1 if save else 0))
        logging.notice(
            "QPCR: %dQ/%dD [%s], dilution:%.1fx, primers: [%s]" %
            (len(src) * len(primers) * nreplicates, ndil,
             ",".join([s.name
                       for s in src]) if names is None else ",".join(names),
             needDil, ",".join(primers)))

        for svi in range(len(sv)):
            s = sv[svi]
            if s.hasBeads:
                prereqs = []
            else:
                j0 = self.jobq.addShake(sample=s, prereqs=[])
                prereqs = [j0]
            intermed = s

            for i in range(nstages):
                dil = math.pow(needDil, 1.0 / nstages)
                #print "stage ",i,", needDil=",needDil,", dil=",dil
                if i > 0:
                    vol = self.MAXDILVOL
                else:
                    vol = min(self.MAXDILVOL,
                              max(self.MINDILVOL, dil * self.TGTINVOL))
                if intermed.plate == decklayout.DILPLATE:
                    firstWell = intermed.well + 4  # Skip by 4 wells at a time to optimize multi-tip movements
                else:
                    firstWell = 0
                if not save and i == 0 and names is not None:
                    # Need to replace the name in this condition
                    dest = Sample(diluteName(names[svi], dil),
                                  decklayout.DILPLATE,
                                  firstWell=firstWell)
                else:
                    dest = Sample(diluteName(intermed.name, dil),
                                  decklayout.DILPLATE,
                                  firstWell=firstWell)
                #print "dest=",dest
                j1 = self.jobq.addMultiTransfer(volume=vol * (dil - 1) / dil,
                                                src=self.dilutant,
                                                dest=dest,
                                                prereqs=[])
                prereqs.append(j1)
                j2 = self.jobq.addTransfer(volume=vol / dil,
                                           src=intermed,
                                           dest=dest,
                                           prereqs=prereqs)
                #print "Dilution of %s was %.2f instead of %.2f (error=%.0f%%)"%(dest.name,(dil/(1+dil))/(1/dil),dil,((dil/(1+dil))/(1/dil)/dil-1)*100)
                if dest.hasBeads:
                    prereqs = [j2]
                else:
                    j3 = self.jobq.addShake(sample=dest, prereqs=[j2])
                    prereqs = [j3]
                intermed = dest
            self.dilProds = self.dilProds + [intermed]
            self.primers = self.primers + [primers]
            self.nreplicates = self.nreplicates + [nreplicates]

    def allprimers(self):
        return set([p for sublist in self.primers for p in sublist])

    def addReferences(self,
                      mindil=1,
                      nsteps=6,
                      dstep=4,
                      nreplicates=1,
                      ref=None,
                      primers=None):
        'Add all needed references'
        #print "addReferences(mindil=",mindil,", nsteps=",nsteps,", dstep=",dstep,", nrep=", nreplicates, ", ref=",ref,")"
        # Make sure the ref reagent is loaded
        if ref is None:
            ref = reagents.getsample("QPCRREF")
        if primers is None:
            primers = self.allprimers()
        dils = [1]
        for i in range(nsteps):
            needDil = mindil * math.pow(dstep, i)
            srcDil = 1
            src = [ref]
            for j in range(len(dils)):
                if needDil / dils[j] <= self.MAXDIL:
                    srcDil = dils[j]
                    if srcDil == 1:
                        src = [ref]
                    else:
                        srcname = "%s.D%d" % (ref.name, srcDil)
                        src = [Sample.lookup(srcname)]
                        if src[0] is None:
                            src = [Sample(srcname, decklayout.DILPLATE)]
                    break
            tmp = self.MINDILVOL
            self.MINDILVOL = 75  # Make sure there's enough for resuing dilutions
            self.addSamples(src=src,
                            needDil=needDil / srcDil,
                            primers=primers,
                            nreplicates=nreplicates,
                            save=needDil / srcDil > self.MAXDIL,
                            saveVol=75)
            self.MINDILVOL = tmp
            dils.append(needDil)

        self.addSamples(src=[self.dilutant],
                        needDil=1,
                        primers=primers,
                        nreplicates=nreplicates,
                        save=False)

    def idler(self, t):
        endTime = clock.elapsed() + t
        if self.debug:
            print "Idler(%.0f)" % t
        while clock.elapsed() < endTime:
            j = self.jobq.getJob()
            if j is None:
                break
            self.jobq.execJob(self.trp.e, j)
        if self.debug:
            print "Idler done with ", endTime - clock.elapsed(
            ), " seconds remaining"

    def run(self):
        'Run the dilutions and QPCR setup'
        # Setup qPCRs
        #self.jobq.dump()
        self.idler(100000)
        self.trp.e.waitpgm(
        )  # May still need to wait for PTC to complete before able to do final jobs
        self.idler(100000)

        if self.jobq.len() > 0:
            logging.error("Blocked jobs remain on queue:", fatal=False)
            self.jobq.dump()
            assert False

        tgt1 = Sample("Barcoded.Mixdown1", decklayout.EPPENDORFS)
        for i in range(1, len(self.dilProds)):
            self.e.transfer(12.5 * 1.2 * 2 / 1.5,
                            self.dilProds[i],
                            tgt1,
                            mix=(False, False))
Example #15
0
class TRP(object):
    def __init__(self):
        'Create a new TRP run'
            
    def reset(self):
        'Reset this experiment so we can generate it again after adjusting the reagent initial volumes and total time'
        totalTime=clock.elapsed()
        clock.reset(totalTime)
        #print "After reset, elapsed=%d"%clock.elapsed()
        worklist.reset()
        self.e=Experiment()
        self.e.setreagenttemp(globals.dewpoint)
        self.e.sanitize(3,50)    # Heavy sanitize
        reagents.reset()
        Sample.clearall()
        Plate.reset()
        decklayout.initWellKnownSamples()
        
    def addTemplates(self,names,stockconc,finalconc=None,units="nM",plate=decklayout.EPPENDORFS,looplengths=None,extraVol=30,wellnames=None,initVol=0):
        'Add templates as "reagents", return the list of them'
        if finalconc is None:
            logging.warning("final concentration of template not specified, assuming 0.6x (should add to addTemplates() call")
            [names,stockconc]=listify([names,stockconc])
            finalconc=[0.6*x for x in stockconc]
        else:
            [names,stockconc,finalconc]=listify([names,stockconc,finalconc])
        if len(set(names))!=len(names):
            logging.error("addTemplates: template names must be unique")
            
        r=[]
        if looplengths is not None:
            assert(len(names)==len(looplengths))
        for i in range(len(names)):
            if wellnames is None:
                well=None
            else:
                well=wellnames[i]
            if reagents.isReagent(names[i]):
                r.append(reagents.lookup(names[i]))
            elif looplengths is None:
                r.append(reagents.add(names[i],plate=plate,conc=Concentration(stockconc[i],finalconc[i],units),extraVol=extraVol,well=well,initVol=initVol))
            else:
                r.append(reagents.add(names[i],plate=plate,conc=Concentration(stockconc[i],finalconc[i],units),extraVol=extraVol,extrainfo=looplengths[i],well=well,initVol=initVol))
                
        return r
    
    def finish(self):
        self.e.lihahome()
        worklist.userprompt("Process complete. Continue to turn off reagent cooler")
        self.e.setreagenttemp(None)

        #Sample.printallsamples("At completion")
        hasError=False
        for s in Sample.getAllOnPlate():
            if s.volume<1.0 and s.conc is not None and not s.emptied:
                logging.error("Insufficient volume for %s: need at least %.1f ul additional"%(s.name,1.0-s.volume),fatal=False)
                #hasError=True
            elif s.volume<2.5 and s.conc is not None and not s.emptied:
                logging.warning("Low final volume for "+ s.name)
            elif s.volume>s.plate.maxVolume:
                logging.erorr("Excess final volume  (%.1f) for %s: maximum is %.1f ul"%(s.volume,s.name,s.plate.maxVolume),fatal=False)
                hasError=True
                
        if hasError:
            logging.error("NO OUTPUT DUE TO ERRORS")
            
        print "Wells used:  samples: %d, dilutions: %d, qPCR: %d"%(Sample.numSamplesOnPlate(decklayout.SAMPLEPLATE),Sample.numSamplesOnPlate(decklayout.DILPLATE),Sample.numSamplesOnPlate(decklayout.QPCRPLATE))
        # Save worklist to a file
        #e.saveworklist("trp1.gwl")
        (scriptname,ext)=os.path.splitext(sys.argv[0])
        self.e.savegem(scriptname+".gem")
        self.e.savesummary(scriptname+".txt")
        Sample.savematlab(scriptname+".m")
        
    ########################
    # Save samples to another well
    ########################
    def saveSamps(self,src,vol,dil,tgt=None,dilutant=None,plate=None,mix=(True,False),atEnd=False):
        [src,vol,dil]=listify([src,vol,dil])
        if plate is None:
            plate=decklayout.REAGENTPLATE
        if tgt is None:
            tgt=[Sample(diluteName(src[i].name,dil[i]),plate,atEnd=atEnd) for i in range(len(src))]

        if any([d!=1.0 for d in dil]):
            if dilutant is None:
                dilutant=decklayout.WATER
            self.e.multitransfer([vol[i]*(dil[i]-1) for i in range(len(vol))],dilutant,tgt,(False,False))

        self.e.shakeSamples(src,returnPlate=True)
        for i in range(len(src)):
            self.e.transfer(vol[i],src[i],tgt[i],mix)
            tgt[i].conc=src[i].conc
            tgt[i].conc.stock=tgt[i].conc.stock/dil[i]
            
        return tgt
    
    def distribute(self,src,dil,vol,wells,tgt=None,dilutant=None,plate=decklayout.SAMPLEPLATE):
        if tgt is None:
            tgt=[Sample("%s.dist%d"%(src[0].name,j),plate) for j in range(wells)]
        
        if dilutant is None:
            dilutant=decklayout.WATER
        self.e.multitransfer([vol*(dil-1) for i in range(wells)],dilutant,tgt)
        self.e.multitransfer([vol for i in range(wells)],src[0],tgt)
        return tgt


    ########################
    # Dilute samples in place
    ########################
    def diluteInPlace(self,tgt,dil=None,finalvol=None):
        # Dilute in place
        # e.g.: trp.diluteInPlace(tgt=rt1,dil=2)
        [tgt,dil,finalvol]=listify([tgt,dil,finalvol])
        dilutant=decklayout.WATER
        for i in range(len(tgt)):
            if finalvol[i] is not None and dil[i] is None:
                self.e.transfer(finalvol[i]-tgt[i].volume,dilutant,tgt[i],mix=(False,False))
            elif finalvol[i] is None and dil[i] is not None:
                self.e.transfer(tgt[i].volume*(dil[i]-1),dilutant,tgt[i],mix=(False,False))
            else:
                logging.error("diluteInPlace: cannot specify both dil and finalvol")

        #print "after dilute, tgt[0]=",str(tgt[0]),",mixed=",tgt[0].isMixed()
        return tgt   #  The name of the samples are unchanged -- the predilution names

    ########################
    # Run a reaction in place
    ########################
    def runRxInPlace(self,src,vol,master,master2=None,master3=None,returnPlate=True,finalx=1.0):
        'Run reaction on beads in given total volume'
        [vol,src,master,master2,master3]=listify([vol,src,master,master2,master3])
        mastervol=[vol[i]*finalx/master[i].conc.dilutionneeded() for i in range(len(vol))]
        master2vol=[0 if master2[i] is None else vol[i]*finalx/master2[i].conc.dilutionneeded() for i in range(len(vol))]
        master3vol=[0 if master3[i] is None else vol[i]*finalx/master3[i].conc.dilutionneeded() for i in range(len(vol))]
        watervol=[vol[i]-src[i].volume-mastervol[i]-master2vol[i]-master3vol[i] for i in range(len(vol))]
        if any([w < -0.02 for w in watervol]):
            logging.error("runRxInPlace: negative amount of water needed: %.2f"%min(watervol))

        for i in range(len(src)):
            if  watervol[i]+src[i].volume>=4.0 and watervol[i]>0.1:
                self.e.transfer(watervol[i],decklayout.WATER,src[i],(False,False))
                watervol[i]=0
        for i in range(len(src)):
            self.e.transfer(mastervol[i],master[i],src[i],(True,False))
        for i in range(len(src)):
            if master2vol[i]>0:
                self.e.transfer(master2vol[i],master2[i],src[i],(True,False))
            if master3vol[i]>0:
                self.e.transfer(master3vol[i],master3[i],src[i],(True,False))
        for i in range(len(src)):
            if  watervol[i]>=0.1:
                self.e.transfer(watervol[i],decklayout.WATER,src[i],(False,False))
        self.e.shakeSamples(src,returnPlate=returnPlate)

    ########################
    # T7 - Transcription
    ########################
    def runT7Setup(self,src,vol,srcdil,ligands=None,tgt=None,rlist=["MT7"]):
        if isinstance(ligands,bool):
            if not ligands:
                ligands=None
            else:
                assert('runT7Setup:  ligands arg should be ligand samples or None, not True')
                
        [ligands,src,tgt,srcdil]=listify([ligands,src,tgt,srcdil])
        for i in range(len(src)):
            if tgt[i] is None:
                if ligands[i] is not None:
                    tgt[i]=Sample("%s.T+%s"%(src[i].name,ligands[i].name),decklayout.SAMPLEPLATE)
                else:
                    tgt[i]=Sample("%s.T-"%src[i].name,decklayout.SAMPLEPLATE)


        worklist.comment("runT7: source=%s"%[str(s) for s in src])

        rvols=[reagents.getsample(x).conc.volneeded(vol) for x in rlist]
        rtotal=sum(rvols)
        sourcevols=[vol*1.0/s for s in srcdil]
        ligandvols=[0 for s in srcdil]
        watervols=[0 for s in srcdil]
        for i in range(len(srcdil)):
            if ligands[i] is not None:
                ligandvols[i]=vol*1.0/ligands[i].conc.dilutionneeded()
                watervols[i]=vol-ligandvols[i]-sourcevols[i]-rtotal
            else:
                watervols[i]=vol-sourcevols[i]-rtotal

        if any([w<-.01 for w in watervols]):
            logging.error("runT7Setup: Negative amount of water required: "+str(watervols))

        if sum(watervols)>0.01:
            self.e.multitransfer(watervols,decklayout.WATER,tgt)
        for ir in range(len(rlist)):
            self.e.multitransfer([rvols[ir] for s in tgt],reagents.getsample(rlist[ir]),tgt)
        for i in range(len(ligands)):
            if ligandvols[i] > 0.01:
                self.e.transfer(ligandvols[i],ligands[i],tgt[i])
        for i in range(len(src)):
            self.e.transfer(sourcevols[i],src[i],tgt[i])
        self.e.shakeSamples(tgt,returnPlate=True)
        for t in tgt:
            t.ingredients['BIND']=1e-20*sum(t.ingredients.values())
        return tgt
    
    def runT7Pgm(self,vol,dur):
        if dur<100:
            pgm="TRP37-%d"%dur
        else:
            pgm="T37-%d"%dur
        worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@37,%d TEMP@25,2'%(pgm,dur*60))
        print "Running T7 at 37C for %d minutes"%dur
        self.e.runpgm(pgm,dur, False,vol)

    def runT7Stop(self,theo,tgt,stopmaster=None,srcdil=2):
        [theo,tgt,stopmaster,srcdil]=listify([theo,tgt,stopmaster,srcdil])
        assert( stopmaster is not None)
            
        ## Stop
        sstopmaster=[reagents.getsample(s) for s in stopmaster]
        for i in range(len(tgt)):
            stopvol=tgt[i].volume/(sstopmaster[i].conc.dilutionneeded()-1)
            finalvol=tgt[i].volume+stopvol
            tgt[i].conc=Concentration(finalvol/tgt[i].volume,1)  # Adjust source dilution to avoid warnings
            self.e.transfer(finalvol-tgt[i].volume,sstopmaster[i],tgt[i])
            
        self.e.shakeSamples(tgt,returnPlate=True)

        return tgt

    def addEDTA(self,tgt,finalconc=4):
        edta=reagents.getsample("EDTA")
        edta.conc.final=finalconc
        srcdil=edta.conc.stock*1.0/(edta.conc.stock-finalconc)
        for t in tgt:
            t.conc=Concentration(srcdil,1)
            v=t.volume*finalconc/(edta.conc.stock-finalconc)
            self.e.transfer(v,edta,t,mix=(False,False))
        self.e.shakeSamples(tgt,returnPlate=True)
        
    def runT7(self,theo,src,vol,srcdil,tgt=None,dur=15,stopmaster=None):
        [theo,src,tgt,srcdil,stopmaster]=listify([theo,src,tgt,srcdil,stopmaster])
        tgt=self.runT7Setup(theo,src,vol,srcdil,tgt)
        self.runT7Pgm(vol,dur)
        tgt=self.runT7Stop(theo,tgt,stopmaster)
        return tgt

    ########################
    # Beads
    ########################
    def bindBeads(self,src,beads=None,beadConc=None,beadDil=None,bbuffer=None,incTime=60,addBuffer=False):
        if beads is None:
            beads=reagents.getsample("Dynabeads")
        if bbuffer is None:
            bbuffer=reagents.getsample("BeadBuffer")
            
        [src,beads,bbuffer,beadConc,beadDil]=listify([src,beads,bbuffer,beadConc,beadDil])

        for s in src:
            if s.plate!=decklayout.SAMPLEPLATE:
                logging.error( "runBeadCleanup: src "+s.name+" is not in sample plate.")

            s.conc=None		# Can't track concentration of beads
            
        self.e.moveplate(src[0].plate,"Home")		# Make sure we do this off the magnet

        # Calculate volumes needed
        beadDil=[beads[i].conc.stock/(beads[i].conc.final if beadConc[i] is None else beadConc[i]) if beadDil[i] is None else beadDil[i] for i in range(len(beads))]

        if addBuffer:
            totalvol=[s.volume/(1-1.0/beadDil-1.0/bbuffer[i].conc.dilutionneeded()) for s in src]
            buffervol=[totalvol[i]/bbuffer[i].conc.dilutionneeded() for i in range(len(src))]
            # Add binding buffer to bring to 1x (beads will already be in 1x, so don't need to provide for them)
            for i in range(len(src)):
                self.e.transfer(buffervol[i],bbuffer[i],src[i])
        else:
            buffervol=[0.0 for i in range(len(src))]
            totalvol=[src[i].volume/(1-1.0/beadDil[i]) for i in range(len(src))]

        beadvol=[totalvol[i]/beadDil[i] for i in range(len(totalvol))]

        # Transfer the beads
        for i in range(len(src)):
            self.e.transfer(beadvol[i],beads[i],src[i],(True,False))	# Mix beads before

        self.e.shakeSamples(src,dur=incTime,returnPlate=False)

    def sepWait(self,src,sepTime=None):
        if sepTime is None:
            maxvol=max([s.volume for s in src])
            if maxvol > 50:
                sepTime=50
            else:
                sepTime=30
            sepTime=120
        self.e.pause(sepTime)	# Wait for separation
        
    def beadWash(self,src,washTgt=None,sepTime=None,residualVolume=0.1,keepWash=False,numWashes=2,wash=None,washVol=50,keepFinal=False,finalTgt=None,keepVol=4.2,keepDil=5,shakeWashes=False):
        # Perform washes
        # If keepWash is true, retain all washes (combined)
        # If keepFinal is true, take a sample of the final wash (diluted by keepDil)
        if wash is None:
            wash=decklayout.WATER
        [src,wash]=listify([src,wash])
        # Do all washes while on magnet
        assert(len(set([s.plate for s in src]))==1)	# All on same plate
        if keepWash:
            if washTgt is None:
                washTgt=[]
                for i in range(len(src)):
                    if s[i].volume-residualVolume+numWashes*(washVol-residualVolume) > decklayout.DILPLATE.maxVolume-20:
                        logging.notice("Saving %.1f ul of wash in eppendorfs"%(numWashes*washVol))
                        washTgt.append(Sample("%s.Wash"%src[i].name,decklayout.EPPENDORFS))
                    else:
                        washTgt.append(Sample("%s.Wash"%src[i].name,decklayout.DILPLATE))

        if keepFinal:
            if finalTgt is None:
                finalTgt=[]
                for i in range(len(src)):
                    finalTgt.append(Sample("%s.Final"%src[i].name,decklayout.DILPLATE))

        if any([s.volume>residualVolume for s in src]):
            # Separate and remove supernatant
            self.e.moveplate(src[0].plate,"Magnet")	# Move to magnet
            self.sepWait(src,sepTime)

            # Remove the supernatant
            for i in range(len(src)):
                if src[i].volume > residualVolume:
                    amt=src[i].amountToRemove(residualVolume)
                    if keepWash:
                        self.e.transfer(amt,src[i],washTgt[i])	# Keep supernatants
                        washTgt[i].conc=None	# Allow it to be reused
                    else:
                        self.e.dispose(amt,src[i])	# Discard supernatant
                
        # Wash

        for washnum in range(numWashes):
            if src[0].plate.curloc!="Home" and src[0].plate.curloc!="Magnet":
                self.e.moveplate(src[0].plate,"Home")
            if keepFinal and washnum==numWashes-1:
                'Retain sample of final'
                for i in range(len(src)):
                    src[i].conc=None
                    self.e.transfer(washVol-src[i].volume,wash[i],src[i],mix=(False,True))	# Add wash
                self.e.shakeSamples(src,returnPlate=True)
                self.saveSamps(src=src,tgt=finalTgt,vol=keepVol,dil=keepDil,plate=decklayout.DILPLATE)
            else:
                for i in range(len(src)):
                    src[i].conc=None
                    self.e.transfer(washVol-src[i].volume,wash[i],src[i],mix=(False,False))	# Add wash, no need to pipette mix since some heterogenity won't hurt here
                if shakeWashes:
                    self.e.shakeSamples(src,returnPlate=False)

            self.e.moveplate(src[0].plate,"Magnet")	# Move to magnet
                
            self.sepWait(src,sepTime)
                
            for i in range(len(src)):
                amt=src[i].amountToRemove(residualVolume)
                if keepWash:
                    self.e.transfer(amt,src[i],washTgt[i],mix=(False,False))	# Remove wash
                    washTgt[i].conc=None	# Allow it to be reused
                else:
                    self.e.dispose(amt,src[i])	# Remove wash

        #self.e.moveplate(src[0].plate,"Home")

        # Should only be residualVolume left with beads now
        result=[]
        if keepWash:
            result=result+washTgt
        if keepFinal:
            result=result+finalTgt

        return result

    def beadAddElutant(self,src,elutant=None,elutionVol=30,eluteTime=60,returnPlate=True,temp=None):
        if elutant is None:
            elutant=decklayout.WATER
        [src,elutionVol,elutant]=listify([src,elutionVol,elutant])
        for i in range(len(src)):
            if elutionVol[i]<30:
                logging.warning("elution from beads of %s with %.1f ul < minimum of 30ul"%(src[i].name,elutionVol[i]))
            self.e.moveplate(src[i].plate,"Home")
            self.e.transfer(elutionVol[i]-src[i].volume,elutant[i],src[i],(False,True))	
        if temp is None:
            for plate in set([s.plate for s in src]):
                self.e.shake(plate,dur=eluteTime,returnPlate=returnPlate,force=True)
        else:
            self.e.shakeSamples(src,dur=30,returnPlate=False)
            worklist.pyrun('PTC\\ptcsetpgm.py elute TEMP@%d,%d TEMP@25,2'%(temp,eluteTime))
            self.e.runpgm("elute",eluteTime/60,False,elutionVol[0])
            if returnPlate:
                self.e.moveplate(src[0].plate,"Home")

    def beadSupernatant(self,src,tgt=None,sepTime=None,residualVolume=0.1,plate=None):
        if plate is None:
            plate=decklayout.SAMPLEPLATE
        if tgt is None:
            tgt=[]
            for i in range(len(src)):
                tgt.append(Sample("%s.SN"%src[i].name,plate))
        [src,tgt]=listify([src,tgt])

        if any([s.plate!=src[0].plate for s in src]):
            logging.error("beadSupernatant: Attempt to magsep on multiple plates at the same time")

        self.e.moveplate(src[0].plate,"Magnet")	# Move to magnet
        self.sepWait(src,sepTime)

        for i in range(len(src)):
            self.e.transfer(src[i].amountToRemove(residualVolume),src[i],tgt[i],(False,False))	# Transfer elution to new tube

        self.e.moveplate(src[0].plate,"Home")
        return tgt

    def beadCombine(self,src,residualVolume=0.1,suspendVolume=150,sepTime=None):
        'Combine everything in the src wells into a the first well; assumes that there are enough beads in that well for all the combination'
        tgt=src[0]
        for s in src[1:]:
            # Combine s with tgt
            if tgt.volume>residualVolume:
                self.e.moveplate(tgt.plate,"Magnet")	# Move to magnet
                self.sepWait([tgt],sepTime)
                self.e.dispose(tgt.amountToRemove(residualVolume),tgt)
            self.e.moveplate(tgt.plate,"Home")	
            if s.volume<suspendVolume:
                self.e.transfer(suspendVolume-s.volume,decklayout.WATER,s,(False,False))
            vol=s.volume-residualVolume-1
            s.conc=None
            self.e.transfer(vol,s,tgt,mix=(True,True))

        self.e.moveplate(tgt.plate,"Home")	
        return src[0:1]
    
    ########################
    # Ampure Cleanup
    ########################
    def runAmpure(self,src,ratio,tgt=None,incTime=5*60,elutionVol=None,evapTime=2*60):
        if elutionVol is None:
            elutionVol=src[0].volume
        self.bindBeads(src=src,beads=reagents.getsample("Ampure"),beadDil=(ratio+1)/ratio,incTime=incTime)
        self.beadWash(src=src,wash=reagents.getsample("EtOH80"),washVol=100,numWashes=2)
        self.e.pause(evapTime)	# Wait for evaporation
        self.beadAddElutant(src=src,elutant=reagents.getsample("TE8"),elutionVol=elutionVol)
        tgt=self.beadSupernatant(src=src,sepTime=120,tgt=[Sample("%s.ampure"%r.name,decklayout.SAMPLEPLATE) for r in src])
        return tgt
    
    ########################
    # RT - Reverse Transcription
    ########################
    def runRT(self,src,vol,srcdil,tgt=None,dur=20,heatInactivate=False,hiTemp=None,incTemp=37,stop=None):
        result=self.runRTSetup(src,vol,srcdil,tgt,stop=stop)
        self.runRTPgm(dur,heatInactivate=heatInactivate,hiTemp=hiTemp,incTemp=incTemp)
        return result
    
    def runRTInPlace(self,src,vol,dur=20,heatInactivate=False,hiTemp=None,incTemp=37):
        'Run RT on beads in given volume'

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=None

        self.runRxInPlace(src,vol,reagents.getsample("MPosRT"),returnPlate=False)
        self.runRTPgm(dur,heatInactivate=heatInactivate,hiTemp=hiTemp,incTemp=incTemp)
        
    def runRTSetup(self,src,vol,srcdil,tgt=None,rtmaster=None,stop=None):
        if rtmaster is None:
            rtmaster=reagents.getsample("MPosRT")
        if tgt is None:
            tgt=[Sample(s.name+".RT+",decklayout.SAMPLEPLATE) for s in src]

        [src,tgt,vol,srcdil,stop]=listify([src,tgt,vol,srcdil,stop])

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=Concentration(srcdil[i],1)
            
        stopvol=[ 0 if stop[i] is None else vol[i]/stop[i].conc.dilutionneeded() for i in range(len(vol))]
        assert(min(stopvol)==max(stopvol))   # Assume all stop volumes are the same
        self.e.stage('RTPos',[rtmaster],[src[i] for i in range(len(src)) ],[tgt[i] for i in range(len(tgt)) ],[vol[i]-stopvol[i] for i in range(len(vol))],destMix=False,finalx=vol[0]/(vol[0]-stopvol[0]))
        for i in range(len(tgt)):
            if stopvol[i]>0.1:
                self.e.transfer(stopvol[i],stop[i],tgt[i],(False,False))

        #self.e.shakeSamples(tgt,returnPlate=True)
        return tgt

    def runRTPgm(self,dur=20,heatInactivate=False,hiTemp=None,incTemp=37):
        pgm="RT-%d"%dur
        if heatInactivate:
            if hiTemp is None:
                hiTemp=95
                print "Assuming RT heat inactivation temperature of ",hiTemp
            hidur=2
            worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@%d,%d TEMP@%d,%d TEMP@25,2 RATE 0.5'%(pgm,incTemp,dur*60,hiTemp,hidur*60))
            self.e.runpgm(pgm,dur+hidur+2.5,False,100)		# Volume doesn't matter since it's just an incubation, use 100ul
            print "Running RT at %dC for %d min, followed by heat inactivation/refold at %dC for %d minutes"%(incTemp,dur,hiTemp,hidur)
        else:
            worklist.pyrun('PTC\\ptcsetpgm.py %s TEMP@%d,%d TEMP@25,2'%(pgm,incTemp,dur*60))
            self.e.runpgm(pgm,dur,False,100)		# Volume doesn't matter since it's just an incubation, use 100ul
            print "Running RT at %dC for %d min without heat inactivation"%(incTemp,dur)
 
    ########################
    # Incubation - run a single temp incubation followed by inactivation
    ########################
    def runIncubation(self,src=None,vol=None,srcdil=None,tgt=None,enzymes=None,incTemp=37,incTime=15,hiTemp=None,hiTime=0,inPlace=False):
        if len(enzymes)!=1:
            logging.error("runIncubation only supports a single master mix")
        if inPlace:
            if tgt is not None:
                logging.error("tgt specified for in-place incubation")
        elif tgt is None:
            tgt=[Sample("%s.%s"%(src[i].name,enzymes[0].name),decklayout.SAMPLEPLATE) for i in range(len(src))]

        if srcdil==None:
            # Minimum dilution (no water)
            srcdil=1/(1-sum([1/e.conc.dilutionneeded() for e in enzymes]))

        if vol is None and inPlace:
            vol=[s.volume*srcdil for s in src]
            
        [src,tgt,vol,srcdil]=listify([src,tgt,vol,srcdil])

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=Concentration(srcdil[i],1)

        if inPlace:
            self.runRxInPlace(src,vol,enzymes[0],returnPlate=False)
            tgt=src
        else:
            self.e.stage('Incubation',enzymes,src,tgt,vol,destMix=False)
            self.e.shakeSamples(tgt,returnPlate=(incTime is None))

        if incTime is None:
            print "Setup only of incubation with %s"%enzymes[0].name
        else:
            if hiTemp is None:
                worklist.pyrun('PTC\\ptcsetpgm.py INC TEMP@%.0f,%.0f TEMP@25,30'%(incTemp,incTime*60))
                print "Incubating at %dC for %d minutes without heat inactivation"%(incTemp, incTime)
                hiTime=0
            else:
                assert(hiTime>0)
                worklist.pyrun('PTC\\ptcsetpgm.py INC TEMP@%.0f,%.0f TEMP@%.0f,%.0f TEMP@25,30'%(incTemp,incTime*60,hiTemp,hiTime*60))
                print "Incubating at %dC for %d minutes followed by heat inactivate at %dC for %d minutes"%(incTemp,incTime,hiTemp,hiTime)
            self.e.runpgm("INC",incTime+hiTime+2,False,max(vol),hotlidmode="TRACKING",hotlidtemp=10)

        return tgt

    ########################
    # USER - USER enzyme digestion
    ########################
    def runUser(self,src=None,vol=None,srcdil=None,tgt=None,incTime=15,inPlace=False):
        return self.runIncubation(src=src,vol=vol,srcdil=srcdil,tgt=tgt,incTemp=37,incTime=incTime,enzymes=[reagents.getsample("MUser")],inPlace=inPlace)
        
    ########################
    # EXO - EXO enzyme digestion
    ########################
    def runExo(self,src=None,vol=None,srcdil=None,tgt=None,incTime=15,inPlace=False):
        return self.runIncubation(src=src,vol=vol,srcdil=srcdil,tgt=tgt,incTemp=37,incTime=incTime,enzymes=[reagents.getsample("MExo")],inPlace=inPlace,hiTemp=80,hiTime=20)
        
    ########################
    # Klenow extension
    ########################
    def runKlenow(self,src=None,vol=None,srcdil=None,tgt=None,incTime=15,hiTime=20,hiTemp=75,inPlace=False):
        assert(inPlace or vol is not None)
        return self.runIncubation(src=src,vol=vol,srcdil=srcdil,tgt=tgt,incTemp=37,incTime=incTime,hiTemp=hiTemp,hiTime=hiTime,enzymes=[reagents.getsample("MKlenow")],inPlace=inPlace)

    ########################
    # Ligation
    ########################
    def runLig(self,src=None,vol=None,srcdil=None,tgt=None,incTime=15,hiTime=10,hiTemp=65,inPlace=False):
        assert(inPlace or vol is not None)
        return self.runIncubation(src=src,vol=vol,srcdil=srcdil,tgt=tgt,incTemp=37,incTime=incTime,hiTemp=hiTemp,hiTime=hiTime,enzymes=[reagents.getsample("MLigase")],inPlace=inPlace)

    ########################
    # DNase digestion
    ########################
    def runDNase(self,src=None,vol=None,srcdil=None,tgt=None,incTime=15,hiTime=10,inPlace=False):
        return self.runIncubation(src=src,vol=vol,srcdil=srcdil,tgt=tgt,incTemp=37,incTime=incTime,hiTemp=75,hiTime=hiTime,enzymes=[reagents.getsample("MDNase")],inPlace=inPlace)

    ########################
    # PCR
    ########################
    def runPCR(self,primers,src,srcdil,vol=None,tgt=None,ncycles=20,usertime=None,fastCycling=False,inPlace=False,master="MTaq",annealTemp=None,kapa=False):
        ## PCR
        if inPlace:
            if vol!=None:
                print "runPCR: cannot specify volume when using inPlace=True, srcdil and input volume determine reaction volume"
                assert(False)
            if tgt!=None:
                print "runPCR: cannot specify tgt when using inPlace=True"
                assert(False)
            [primers,src,vol,srcdil]=listify([primers,src,vol,srcdil])
            vol=[src[i].volume*srcdil[i] for i in range(len(src))]
            tgt=src
        else: 
            [primers,src,tgt,vol,srcdil]=listify([primers,src,tgt,vol,srcdil])
            for i in range(len(tgt)):
                if tgt[i] is None:
                    if isinstance(primers[i],list):
                        tgt[i]=Sample("%s.P%s"%(src[i].name,"+".join(primers[i])),src[i].plate)
                    else:
                        tgt[i]=Sample("%s.P%s"%(src[i].name,primers[i]),src[i].plate)

        # Adjust source dilution
        for i in range(len(src)):
            src[i].conc=Concentration(srcdil[i],1)
        
        logging.notice( "primer="+str(primers))

        # Add reagent entries for any missing primers
        if isinstance(primers[0],list):
            allprimers=[x for y in primers for x in y]
        else:
            allprimers=primers
        for up in set(allprimers):
            s="P-%s"%up
            if not reagents.isReagent(s):
                reagents.add(name=s,conc=4,extraVol=30)

        if isinstance(primers[0],list):
            # Multiple primers
            if inPlace:
                assert len(primers[0])==2
                self.runRxInPlace(src,vol,reagents.getsample(master),master2=[reagents.getsample("P-%s"%p[0]) for p in primers],master3=[reagents.getsample("P-%s"%p[1]) for p in primers],returnPlate=False)
            else:
                for i in range(len(primers)):
                    self.e.stage('PCR%d'%i,[reagents.getsample(master)]+[reagents.getsample("P-%s"%s) for s in primers[i]],src[i:i+1] ,tgt[i:i+1],vol[i:i+1],destMix=False)
                #self.e.shakeSamples(tgt,returnPlate=False)
        else:
            # Single primer
            if inPlace:
                self.runRxInPlace(src,vol,reagents.getsample(master),master2=[reagents.getsample("P-%s"%p) for p in primers],returnPlate=False)
            else:
                for up in set(primers):
                    self.e.stage('PCR%s'%up,[reagents.getsample(master),reagents.getsample("P-%s"%up)],[src[i] for i in range(len(src)) if primers[i]==up],[tgt[i] for i in range(len(tgt)) if primers[i]==up],[vol[i] for i in range(len(vol)) if primers[i]==up],destMix=False)
                #self.e.shakeSamples(tgt,returnPlate=False)

        pgm="PCR%d"%ncycles

        if usertime is None:
            runTime=0
        else:
            runTime=usertime

        if annealTemp is None:
            annealTemp=60 if kapa else 57

        meltTemp=98 if kapa else 95
        hotTime=180 if kapa else 30
        extTemp=72 if kapa else 68
        
        if fastCycling:
            cycling='TEMP@37,%d TEMP@95,%d TEMP@%.1f,10 TEMP@%.1f,10 TEMP @%.1f,1 GOTO@3,%d TEMP@%.1f,60 TEMP@25,2'%(1 if usertime is None else usertime*60,hotTime,meltTemp,annealTemp,extTemp,ncycles-1,extTemp)
            runTime+=hotTime/60+2.8+1.65*ncycles
        else:
            cycling='TEMP@37,%d TEMP@95,%d TEMP@%.1f,30 TEMP@%.1f,30 TEMP@%.1f,30 GOTO@3,%d TEMP@%.1f,60 TEMP@25,2'%(1 if usertime is None else usertime*60,hotTime,meltTemp,annealTemp,extTemp,ncycles-1,extTemp)
            runTime+=hotTime/60+2.8+3.0*ncycles
            
        print "PCR volume=[",",".join(["%.1f"%t.volume for t in tgt]), "], srcdil=[",",".join(["%.1fx"%s for s in srcdil]),"], program: %s"%cycling

        worklist.pyrun('PTC\\ptcsetpgm.py %s %s'%(pgm,cycling))
        self.e.runpgm(pgm,runTime,False,max(vol),hotlidmode="CONSTANT",hotlidtemp=100)
        # Mark samples as mixed (by thermal convection)
        print "Marking samples as mixed (by thermal convection)"
        for t in tgt:
            t.wellMixed=True
            t.lastMixed=clock.elapsed()
        #self.e.shakeSamples(tgt,returnPlate=True)
        return tgt

    ########################
    # qPCR
    ########################
    def runQPCRDIL(self,src,vol,srcdil,tgt=None,dilPlate=False,pipMix=False,dilutant=decklayout.SSDDIL):
        [src,vol,srcdil]=listify([src,vol,srcdil])
        vol=[float(v) for v in vol]
        if tgt is None:
            if dilPlate:
                tgt=[Sample(diluteName(src[i].name,srcdil[i]),decklayout.DILPLATE) for i in range(len(src))]
            else:
                tgt=[Sample(diluteName(src[i].name,srcdil[i]),decklayout.SAMPLEPLATE) for i in range(len(src))]

        srcvol=[vol[i]/srcdil[i] for i in range(len(vol))]
        watervol=[vol[i]-srcvol[i] for i in range(len(vol))]
        if len(watervol) > 4 and sum(watervol)>800:
            logging.notice("Could optimize distribution of "+str(len(watervol))+" moves of "+dilutant.name+": vol=["+str(["%.1f"%w for w in watervol])+"]")
        self.e.multitransfer(watervol,dilutant,tgt,(False,False))
        
        self.e.shakeSamples(src,returnPlate=True)
        for i in range(len(src)):
            tgt[i].conc=None		# Assume dilutant does not have a concentration of its own
            # Check if we can align the tips here
            if i<len(src)-3 and tgt[i].well+1==tgt[i+1].well and tgt[i].well+2==tgt[i+2].well and tgt[i].well+3==tgt[i+3].well and tgt[i].well%4==0 and self.e.cleanTips!=15:
                #print "Aligning tips"
                self.e.sanitize()
            self.e.transfer(srcvol[i],src[i],tgt[i],(not src[i].isMixed(),pipMix))
            if tgt[i].conc != None:
                tgt[i].conc.final=None	# Final conc are meaningless now
            
        return tgt
        
    def runQPCR(self,src,vol,primers,nreplicates=1,enzName="EvaUSER"):
        ## QPCR setup
        worklist.comment("runQPCR: primers=%s, source=%s"%([p for p in primers],[s.name for s in src]))
        [src,vol,nreplicates]=listify([src,vol,nreplicates])
        self.e.shakeSamples(src,returnPlate=True)

        # Build a list of sets to be run
        torun=[]
        for repl in range(max(nreplicates)):
            for p in primers:
                for i in range(len(src)):
                    if nreplicates[i]<=repl:
                        continue
                    if repl==0:
                        sampname="%s.Q%s"%(src[i].name,p)
                    else:
                        sampname="%s.Q%s.%d"%(src[i].name,p,repl+1)
                    s=Sample(sampname,decklayout.QPCRPLATE)
                    torun=torun+[(src[i],s,p,vol[i])]

        # Add enzyme
        e=reagents.getsample(enzName)
        v=[a[3]/e.conc.dilutionneeded() for a in torun]
        t=[a[1] for a in torun]
        self.e.multitransfer(v,e,t)

        # Make the target have 'none' concentration so we can multiadd to it again
        for s in t:
            s.conc=None

        # Fill the master mixes
        dil={}
        for p in primers:
            mname="P-%s"%p
            if not reagents.isReagent(mname):
                reagents.add(name=mname,conc=4,extraVol=30)
            mq=reagents.getsample(mname)
            t=[a[1] for a in torun if a[2]==p]
            v=[a[3]/mq.conc.dilutionneeded() for a in torun if a[2]==p]
            assert(v>0)
            self.e.multitransfer(v,mq,t,(False,False))
            dil[p]=1.0/(1-1/e.conc.dilutionneeded()-1/mq.conc.dilutionneeded())
            
        # Add the samples
        self.e.sanitize()		# In case we are aligned
        for a in torun:
            s=a[0]
            t=a[1]
            p=a[2]
            v=a[3]/dil[p]
            t.conc=None		# Concentration of master mix is irrelevant now
            self.e.transfer(v,s,t)
            
        return [a[1] for a in torun]

    def setup(self):
        'Setup for experiment -- run once.  Usually overridden by actual experiment'
        worklist.setOptimization(True)

    def pgm(self):
        'Actual robot code generation -- may be run multiple times to establish initial volumes.  Overridden by actual experiment'

    def run(self):
        parser=argparse.ArgumentParser(description="TRP")
        parser.add_argument('-v','--verbose',help='Enable verbose output',default=False,action="store_true")
        parser.add_argument('-D','--dewpoint',type=float,help='Dew point',default=10.0)
        args=parser.parse_args()
        
        print "Estimating evaporation for dew point of %.1f C"%args.dewpoint
        globals.dewpoint=args.dewpoint

        self.reset()

        self.setup()
        if args.verbose:
            globals.verbose=True
            print '------ Preliminary runs to set volume -----'
        else:
            sys.stdout=open(os.devnull,'w')
        self.pgm()
        self.reset()
        self.pgm()
        if args.verbose:
            print '------ Main run -----'
        else:
            sys.stdout=sys.__stdout__
        self.reset()
        self.pgm()
        self.finish()
Example #16
0
    def getJob(self):
        'Return the next job on the queue to execute, removing it from queue'

        if self.runningJob!=None:
            logging.warning("Call of getJob() while a job is running - returning None")
            return None

        # Remove any shake jobs that are unneeded
        for id,j in self.jobs.items():
            if j['type']=='shake' and len(j['prereqs'])==0 and j['sample'].isMixed() and not Experiment.shakerIsActive():
                #print "Removing unneeded shake job ",id
                self.removeJob(id)

        for id in self.jobs:
            j=self.jobs[id]
            if j['type']!='transfer' or len(j['prereqs'])>0 or  j['src'].plate.curloc!='Home' or  j['dest'].plate.curloc!='Home':
                #if j['type']=='transfer':
                #   print "Can't execute job ",id,": ",j,", curlocs=",j['src'].plate.curloc,", ",j['dest'].plate.curloc
                continue
            return id

        for id,j in self.jobs.iteritems():
            if j['type']!='multitransfer' or len(j['prereqs'])>0 or  j['src'].plate.curloc!='Home'  or  j['dest'].plate.curloc!='Home':
                continue
            # Combine with all other multitransfers from same src
            alldest=[]
            allvol=[]
            for id2,j2 in self.jobs.items():
                if j2['type']!='multitransfer' or len(j2['prereqs'])>0 or j['src']!=j2['src']:
                    continue
                alldest.append(j2['dest'])
                allvol.append(j2['volume'])
                self.removeJob(id2)
            combined=self.addMultiTransfer(volume=allvol,src=j['src'],dest=alldest,prereqs=[])
            return combined

        for id in self.jobs:
            j=self.jobs[id]
            if j['type']!='shake' or len(j['prereqs'])>0 or not j['sample'].plate.curloc=='Home' or Experiment.shakerIsActive():
                continue
            return id
        # Nothing to do
        return None
Example #17
0
import os

from Experiment.sample import Sample
from Experiment.experiment import Experiment
from Experiment.experiment import Concentration

e=Experiment()

e.w.pyrun('PTC\\ptcsetpgm.py TEST TEMP@95,1  TEMP@25,1')
e.runpgm("TEST",0,waitForCompletion=False)
e.waitpgm(sanitize=False)

e.savegem("platemovetest_orig.gem")

os.system("grep -v 'Wash\|reagent tubes' platemovetest_orig.gem  > platemovetest.gem")