Beispiel #1
0
def poll_serial():
    """
	Find ports for the bath, pump, spec, and swapper. 
	Connect and return their objects in a dict.
	"""

    # returns ListPortInfo object, which is a tuple of tuples
    # where [0] of inner tuple is port name
    ports = serial.tools.list_ports.comports(include_links=True)

    devs = dict()
    num_devs = 4

    for port in ports:
        # try to connect bath
        try:
            devs["bath"] = isotemp6200.IsotempController(port[0],
                                                         baud=9600,
                                                         timeout=1)
        except:
            pass
        # try to connect pump
        try:
            devs["pump"] = isco260D.ISCOController(port[0],
                                                   baud=9600,
                                                   timeout=1,
                                                   source=0,
                                                   dest=1)
        except:
            pass
        # try to connect spec
        try:
            devs["spec"] = rf5301PC.IsotempController(port[0],
                                                      baud=9600,
                                                      timeout=1)
        except:
            pass
        # try to connect filter swapper
        try:
            devs["swap"] = filterswapper.FilterController(port[0],
                                                          baud=9600,
                                                          timeout=1)
        except:
            pass

    # report all the devices connected
    print(devs)

    # Throw an error if any device is missing
    if len(devs.keys() < num_devs):
        raise DeviceNotFoundError()

    return devs
Beispiel #2
0
def main(args, stdin, stdout, stderr):
    "Wait for a minimum time or linearity, then run P gradient, writing real-time data."
    
    ## run the experiment
    
    # open output file, do not overwrite!
    with open(args['file_log'], 'x') as hand_log:
    
        # compose and write header
        list_head = ["clock", "watch", "state"] + list(states.head()) + ["T_int", "T_ext", "T_act", "P_act", "intensity"]
        line_head = "\t".join(list_head)
        hand_log.write(line_head + '\n')
        hand_log.flush()
        
        # now we're opening serial connections, which need to be closed cleanly on exit
        try:
            # init instruments
            print("connecting...", file=stderr)
            bath = isotemp6200.IsotempController(port=args['port_bath'])
            print("temperature controller √", file=stderr)
            pump = isco260D.ISCOController(port=args['port_pump'])
            print("pressure controller    √", file=stderr)
            spec = rf5301.RF5301(port=args['port_spec'])
            print("fluorospectrometer     √", file=stderr)
            
            ## hardware init
            print("initializing...", file=stderr)
            # start bath circulator
            print("bath", end=' ', file=stderr)
            # set precision
            prec_bath = 2 # number of decimal places
            while not bath.temp_prec(prec_bath):
                pass
            while not bath.on():
                bath.on(True)
            print('√', file=stderr)
                
            # clear pump and write gradient program
            print("pump", end=' ', file=stderr)
            while not pump.remote():
                pass
            while not pump.clear():
                pass
			# write gradient
			# for now, gradient should be programmed at pump controller
            # get initial volume
            vol_start = False
            while not vol_start:
                vol_start = pump.vol_get()
            print("√ V0 = {} mL".format(vol_start), file=stderr)
                
            # open the shutter, unless in auto
            if not args["auto_shut"]:
                while not spec.shutter(True):
                    pass
            
            # declare async queues
            queue_bath = deque(maxlen=1)
            queue_pump = deque(maxlen=1)
            queue_spec = deque(maxlen=1)
            
            # start polling threads
            # all device instances have RLocks!
            bath_free = threading.Event()
            pump_free = threading.Event()
            spec_free = threading.Event()
            [event.set() for event in (bath_free, pump_free, spec_free)]
            threading.Thread(name="pollbath", target=poll, args=(bath, bath_free, queue_bath)).start()
            threading.Thread(name="pollpump", target=poll, args=(pump, pump_free, queue_pump)).start()
            threading.Thread(name="pollspec", target=poll, args=(spec, spec_free, queue_spec)).start()
            
            ## run experiment
            
            # start experiment timer (i.e. stopwatch)
            time_start = time.time()
            
            time_air_tot = 0
            save_air = True
        
            list_data = [0]*10
            
            # iterate over test states
            for state_num in range(states.shape[0]):
            
                # make dicts for this state, the last, and the next
                # takes about 1 ms
                state_curr = states.iloc[state_num+0].to_dict()
                if state_num: 
                    state_prev = states.iloc[state_num-1].to_dict()
                else:
                    # if first state
                    state_prev = {key: 0 for key in state_curr.keys()}
                    chg_prev = {key: True for key in state_curr.keys()}
                if state_num < states.shape[0]-1:
                    state_next = states.iloc[state_num+1].to_dict()
                else:
                    # if final state
                    state_next = {key: 0 for key in state_curr.keys()}
                    chg_next = {key: True for key in state_curr.keys()}
                
                # which params have changed since previous state?
                chg_prev = {key: (state_curr[key] != state_prev[key]) for key in state_curr.keys()}
                chg_next = {key: (state_curr[key] != state_next[key]) for key in state_curr.keys()}
                
                time_state = time.time() # mark time
                waited = False # did the state have to wait for stability?
                readings = 0 # reset n counter
                
                # status update
                print("state {}/{}:".format(state_num+1, states.shape[0]), file=stderr)
                print(state_curr, file=stderr)
                
                # set temp persistently
                # this is the actual setpoint passed to the waterbath
                temp_set = round(temp_ext2int(temp_act2ext(state_curr['T_set'])), prec_bath)
                temp_tol = round(args['tol_T'] / 1.18052628 * 1.312841332, prec_bath)
                bath_free.clear()
                while not isclose(bath.temp_set(), temp_set):
                    print("setting temperature to {}˚C".format(state_curr['T_set']), file=stderr, end=' ')
                    if bath.temp_set(temp_set): print('√', file=stderr)
                bath_free.set()
                
                # set pres persistently
                pump_free.clear()
                while not isclose(pump.press_set(), state_curr['P_set']):
                    print("setting pressure to {} bar".format(state_curr['P_set']), file=stderr, end=' ')
                    if pump.press_set(state_curr['P_set']): print('√', file=stderr)
                pump_free.set()
                    
                ## set the excitation wavelength
                #while not isclose(spec.ex_wl(), state_curr['wl_ex']):
                #    print("setting excitation WL to {} nm".format(state_curr['wl_ex']), file=stderr, end=' ')
                #    if spec.ex_wl(state_curr['wl_ex']): print('√', file=stderr)
                #    
                ## set the excitation wavelength
                #while not isclose(spec.em_wl(), state_curr['wl_em']):
                #    print("setting emission WL to {} nm".format(state_curr['wl_em']), file=stderr, end=' ')
                #    if spec.ex_wl(state_curr['wl_em']): print('√', file=stderr)
                
                # temporary WL setters
                # Persistence implemented over cycles to improve efficiency;
                # note that this checks the previous data row.
                #NTS 20200719 reimplement list_data as a dict!!!
                if not ((state_curr['wl_ex'] == list_data[4]) and (state_curr['wl_em'] == list_data[5])):
                    spec_free.clear()
                    if (state_curr['wl_ex'] == 340) and (state_curr['wl_em'] == 440):
                        print("setting wavelengths to Laurdan blue", file=stderr, end=' ')
                        if spec.wl_set_laurdan_blu(): print('√', file=stderr)
                    elif (state_curr['wl_ex'] == 340) and (state_curr['wl_em'] == 490):
                        print("setting wavelengths to Laurdan red", file=stderr, end=' ')
                        if spec.wl_set_laurdan_red(): print('√', file=stderr)
                    spec_free.set()
                
                # init a log table for the state
                trails = pd.DataFrame(columns = list_head)
                
                # data logging loop
                data_dict = {}
                # init the data dict. Persistent the first time.
                for dq in (queue_bath, queue_pump, queue_spec):
                    while True:
                        # ensure that *something* gets popped out so the dict is complete
                        try:
                            data_dict.update(dq.popleft())
                            break
                        except:
                            pass
                                
                while True:
                
                    time_cycle = time.time()
                    
                    # DATA FIRST
                    for dq in (queue_bath, queue_pump, queue_spec):
                        try:
                            data_dict.update(dq.popleft())
                            break
                        except:
                            pass
                    # add internal data
                    data_dict.update(
                        {
                            "clock" : time.strftime("%Y%m%d %H%M%S"),
                            "watch" : time.time() - time_start,
                            "state" : state_num,
                            "T_set" : state_curr["T_set"],
                            "P_set" : state_curr["P_set"],
                            #"wl_ex" : state_curr["wl_ex"],
                            #"wl_em" : state_curr["wl_em"]
                        }
                    )
                    # round off the modeled T_act
                    data_dict["T_act"] = round(data_dict["T_act"], prec_bath)
                    
                    # SAFETY SECOND
                    # check for pressure system leak
                    if (data_dict["vol"] - vol_start) > args["vol_diff"]:
                        pump_free.clear()
                        pump.clear()
                        raise Exception("Pump has discharged > {} mL!".format(args["vol_diff"]))
                        
                    # AIR SAVER
                    # if transition's been running for >2x the stability time,
                    # but bath internal temperature still not in range,
                    # shut air off temporarily
                    if args["air_saver"] and \
                        ((time_cycle - time_state) >= 2 * args["eq_min"]) and \
                        (data_dict["T_int"] < temp_set - temp_tol) or \
                        (data_dict["T_int"] > temp_set + temp_tol):
                        if waited and not save_air: print(file=stderr)
                        if not save_air: print("air saver activated", end='', file=stderr)
                        save_air = True
                    else:
                        save_air = False
                        
                    # control the air system
                    #NTS 20200720: pin set executes twice, which is annoying but not fatal
                    if data_dict['T_act'] <= args["dewpoint"] and not save_air:
                        # if it's cold
                        if not data_dict['air']:
                            # and air is off
                            pump_free.clear()
                            if waited: print(file=stderr)
                            print("turning air ON", file=stderr, end=' ')
                            pump.digital(0, 1)
                            print("√", file=stderr)
                            pump_free.set()
                            data_dict['air'] = True
                            # start a timer for air being on
                            time_air = time.time()
                            print("total air time {} s".format(round(time_air_tot)), file=stderr)
                    else:
                        # if it's warm
                        if data_dict['air']:
                            # and air is on
                            pump_free.clear()
                            if waited: print(file=stderr)
                            print("\nturning air OFF", file=stderr, end=' ')
                            pump.digital(0, 0)
                            print("√", file=stderr)
                            pump_free.set()
                            data_dict['air'] = False
                            # add air time to the total
                            time_air_tot += (time.time() - time_air)
                            print("total air time {} s".format(round(time_air_tot)), file=stderr)
                    
                    # does the state change require equilibration?
                    if any([chg_prev[var] for var in args["vars_eq"]]):
                        # if any of the slow params have changed from last state
                        need2wait = True
                    else:
                        need2wait = False
                    
                    # put data in the temporary trailing DF
                    trails = trails.append(data_dict, ignore_index=True)
                    # cut the DF down to within the trailing time
                    trails = trails[trails['watch'] >= trails['watch'].iloc[-1] - args["eq_min"]]
                    
                    # if the fluor reading has changed
                    # this check takes about 0.1 ms
                    if (trails.shape[0] == 1) or (trails["intensity"].iloc[-1] !=  trails["intensity"].iloc[-2]):
                        # write data to file
                        hand_log.write('\t'.join([str(data_dict[col]) for col in list_head])+'\n')
                        hand_log.flush()
                        
                    #NTS 20200719: It would be nice to abstract pressure and temp stability!
                    try:
                        # note that this checks range of the internal temperature, and stability of the actual temperature
                        temp_in_range = ((max(trails["T_int"]) <= temp_set + temp_tol) and (min(trails["T_int"]) >= temp_set - temp_tol) and ((max(trails["T_act"]) - min(trails["T_act"]) <= 2 * args["tol_T"])))
                        pres_in_range = ((max(trails["P_act"]) <= state_curr["P_set"] + args["tol_P"]) and (min(trails["P_act"]) >= state_curr["P_set"] - args["tol_P"]))
                    except:
                        # in case the dataframe is Empty
                        temp_in_range = False
                        pres_in_range = False
                        pass
                    
                    # if we're equilibrated
                    # and in range
                    # or state has timed out
                    # and windows are defogged
                    if ((not need2wait or (time_cycle - time_state) >= args["eq_min"] and \
                       temp_in_range and pres_in_range and not save_air) or \
                       (time_cycle - time_state) >= args["eq_max"]) and \
                       (data_dict['T_act'] > args["dewpoint"] or (time_cycle - time_air) >= args["air_time"]) :
                       
                        if waited: print(file=stderr) # newline
                        waited = False
                        
                        # open the shutter
                        if (not readings) and args["auto_shut"] and any([chg_prev[var] for var in args["vars_eq"]]): 
                            spec_free.clear()
                            while not spec.shutter(True):
                                pass
                            spec_free.set()
                        # take some readings
                        if (trails.shape[0] == 1) or (trails["intensity"].iloc[-1] !=  trails["intensity"].iloc[-2]):
                            if readings: print("reading {}: {} AU\r".format(readings, trails['intensity'].iloc[-1]), end='', file=stderr)
                            readings += 1
                        # break out of loop to next state
                        if (readings > args["n_read"]):
                            if args["auto_shut"] and any([chg_next[var] for var in args["vars_eq"]]):
                                spec_free.clear()
                                while not spec.shutter(False):
                                    pass
                                spec_free.set()
                            print(file=stderr)
                            break
                            
                    else:
                        # what are we waiting for?
                        print("waiting {} s to get {} s of stability\r".format(round(time.time()-time_state), args["eq_min"]), end='', file=stderr)
                        waited = True
                        
                    # prescribed sleep
                    try:
                        time.sleep((args["cycle_time"] / 1000) - (time.time() - (time_cycle)))
                    except:
                        pass
                    
            # shut down when done
            pump_free.clear()
            pump.digital(0,0)
            pump.pause()
            pump.disconnect()
            bath_free.clear()
            bath.on(False)
            bath.disconnect()
            spec_free.clear()
            spec.shutter(False)
            spec.disconnect()
            sys.exit(0)
    
        except:
            pump_free.clear()
            pump.pause()
            pump.digital(0,0) # turn the air off!
            spec_free.clear()
            spec.ack()
            spec.shutter(False)
            traceback.print_exc()
Beispiel #3
0
def main(args, stdin, stdout, stderr):
    "Run a temperature/parameter scan and store output to a file."

    # if args are passed as namespace, convert it to dict
    try:
        args = vars(args)
    except:
        pass

    if not stdin.isatty():
        # if a state table is passed on stdin, read it
        print("reading states from stdin", file=stderr)
        states = pd.read_csv(stdin, sep='\t')
    else:
        print("ERR: you need to pass the state table on stdin!", file=stderr)
        exit(1)

    ## run the experiment

    # open output file, do not overwrite!
    with open(args['file_log'], 'x') as hand_log:

        # variables tracking the expt schedule
        vars_sched = ["clock", "watch", "state"]
        # externally measured and derived variables
        vars_measd = [
            "T_int", "T_ext", "T_act", "P", "I", "D", "P_act", "vol",
            "intensity", "T_amb", "H_amb", "dewpt", "air"
        ]
        # compose and write header - states.head() are the setpoint variables
        list_head = vars_sched + list(states.head()) + vars_measd
        line_head = "\t".join(list_head)
        hand_log.write(line_head + '\n')
        hand_log.flush()

        # now we're opening serial connections, which need to be closed cleanly on exit
        try:
            # init instruments
            print("connecting...", file=stderr)
            print("fluorospectrometer     √", file=stderr)
            amcu = auxmcu.AuxMCU(port=args['port_amcu'])
            print("aux microcontroller    √", file=stderr)
            bath = isotemp6200.IsotempController(port=args['port_bath'])
            print("temperature controller √", file=stderr)
            pump = isco260D.ISCOController(port=args['port_pump'])
            print("pressure controller    √", file=stderr)
            spec = rf5301.RF5301(port=args['port_spec'])

            ## hardware init
            print("initializing...", file=stderr)

            # set spec slits and gain
            print("spec", end='', file=stderr)
            # open the shutter, unless in auto
            if not args["auto_shut"]:
                while not spec.shutter(True):
                    pass
                print('.', end='', file=stderr)
            print(' √', file=stderr)

            # initialize the filter wheels
            print("auxiliary", file=stderr)
            amcu.lamp(True)
            #amcu.wheels_init()

            # start bath circulator
            print("bath", end='', file=stderr)

            # init topside PID
            pid = PID(1, 0, 85, setpoint=states.loc[states.index[0], "T_set"])
            # windup preventer
            pid.output_limits = (-20, 20)
            # enter topside cal coefficients
            bath.cal_ext.reset(*args["rtd_cal"])

            # set controller gains
            while not all(bath.pid('H', 0.8, 0, 0)):
                pass
            print('.', end='', file=stderr)
            while not all(bath.pid('C', 1, 0, 0)):
                pass
            print('.', end='', file=stderr)
            # set precision (number of decimal places)
            while not bath.temp_prec(2):
                pass
            print('.', end='', file=stderr)
            # set controller to listen to external RTD
            while not bath.probe_ext(True):
                pass
            print('.', end='', file=stderr)
            # finally, start the bath
            while not bath.on():
                bath.on(True)
            print(' √', file=stderr)

            # clear and start pump
            print("pump", end='', file=stderr)
            while not pump.remote():
                pass
            print('.', end='', file=stderr)
            while not pump.clear():
                pass
            print('.', end='', file=stderr)
            while not pump.run():
                pass
            print('.', end='', file=stderr)
            # get initial volume
            vol_start = None
            while not vol_start:
                vol_start = pump.vol_get()
            print(" √ V0 = {} mL".format(vol_start), file=stderr)

            # declare async queues
            queue_bath = deque(maxlen=1)
            queue_pump = deque(maxlen=1)
            queue_spec = deque(maxlen=1)
            queue_amcu = deque(maxlen=1)

            # start polling threads
            # all device instances have RLocks!
            bath_free = threading.Event()
            pump_free = threading.Event()
            spec_free = threading.Event()
            amcu_free = threading.Event()
            [
                event.set()
                for event in (bath_free, pump_free, spec_free, amcu_free)
            ]
            threading.Thread(name="pollbath",
                             target=poll,
                             args=(bath, bath_free, queue_bath, pid)).start()
            threading.Thread(name="pollpump",
                             target=poll,
                             args=(pump, pump_free, queue_pump)).start()
            threading.Thread(name="pollspec",
                             target=poll,
                             args=(spec, spec_free, queue_spec)).start()
            threading.Thread(name="pollamcu",
                             target=poll,
                             args=(amcu, amcu_free, queue_amcu)).start()

            ## run experiment

            # dict links setp vars to meas vars
            #NTS 20210113 there's gotta be a better place for these mappings
            setp2meas = {"T_set": "T_act", "P_set": "P_act"}

            # start experiment timer (i.e. stopwatch)
            time_start = time.time()

            # iterate over test states
            for state_num in range(states.shape[0]):

                # make dicts for this state, the last, and the next
                # takes about 1 ms
                state_curr = states.iloc[state_num + 0].to_dict()
                if state_num:
                    state_prev = states.iloc[state_num - 1].to_dict()
                else:
                    # if first state
                    state_prev = {key: 0 for key in state_curr.keys()}
                    chg_prev = {key: True for key in state_curr.keys()}
                if state_num < states.shape[0] - 1:
                    state_next = states.iloc[state_num + 1].to_dict()
                else:
                    # if final state
                    state_next = {key: 0 for key in state_curr.keys()}
                    chg_next = {key: True for key in state_curr.keys()}

                # which params have changed since previous state?
                chg_prev = {
                    key: (state_curr[key] != state_prev[key])
                    for key in state_curr.keys()
                }
                chg_next = {
                    key: (state_curr[key] != state_next[key])
                    for key in state_curr.keys()
                }

                time_state = time.time()  # mark time when state starts
                waited = False  # did the state have to wait for stability?
                sat = False  # did the dye have to relax?
                readings = 0  # reset n counter

                # status update
                print("state {}/{}:".format(state_num + 1, states.shape[0]),
                      file=stderr)
                print(state_curr, file=stderr)

                # data logging loop
                data_dict = {}
                # init the data dict. Persistent the first time.
                for dq in (queue_bath, queue_pump, queue_spec, queue_amcu):
                    while True:
                        # ensure that *something* gets popped out so the dict is complete
                        try:
                            data_dict.update(dq.popleft())
                            break
                        except:
                            pass

                # set temp via topside PID
                while not isclose(pid.setpoint, state_curr['T_set']):
                    print("setting temperature to {}˚C".format(
                        state_curr['T_set']),
                          file=stderr,
                          end=' ')
                    # pid object is picked up by poll()
                    pid.setpoint = state_curr['T_set']
                    print('√', file=stderr)

                # set pressure persistently
                pump_free.clear()
                while not isclose(pump.press_set(), state_curr['P_set']):
                    print("setting pressure to {} bar".format(
                        state_curr['P_set']),
                          file=stderr,
                          end=' ')
                    if pump.press_set(state_curr['P_set']):
                        print('√', file=stderr)
                pump_free.set()

                ## set the excitation wavelength
                #while not isclose(spec.ex_wl(), state_curr['wl_ex']):
                #    print("setting excitation WL to {} nm".format(state_curr['wl_ex']), file=stderr, end=' ')
                #    if spec.ex_wl(state_curr['wl_ex']): print('√', file=stderr)
                #
                ## set the emission wavelength
                #while not isclose(spec.em_wl(), state_curr['wl_em']):
                #    print("setting emission WL to {} nm".format(state_curr['wl_em']), file=stderr, end=' ')
                #    if spec.ex_wl(state_curr['wl_em']): print('√', file=stderr)

                # temporary WL setters
                # Persistence implemented over cycles to improve efficiency;
                # note that this checks the previous data row.
                if not ((state_curr['wl_ex'] == data_dict['wl_ex']) and
                        (state_curr['wl_em'] == data_dict['wl_em'])):
                    #if not (isclose(spec.ex_wl(), state_curr['wl_ex']) and isclose(spec.em_wl(), state_curr['wl_em'])):
                    if (state_curr['wl_ex'] == 350) and (state_curr['wl_em']
                                                         == 428):
                        print("setting wavelengths for DPH",
                              file=stderr,
                              end=' ')
                        spec_free.clear()
                        spec.wl_set_dph()
                        spec_free.set()
                        print('√', file=stderr)

                # set polarizers
                if not ((state_curr['pol_ex'] == data_dict['pol_ex']) and
                        (state_curr['pol_em'] == data_dict['pol_em'])):
                    amcu_free.clear()
                    # take the line from other thread!
                    amcu.__ser__.send_break(duration=amcu.__ser__.timeout +
                                            0.1)
                    time.sleep(amcu.__ser__.timeout + 0.1)
                    # excitation
                    if state_curr['pol_ex'] != data_dict['pol_ex']:
                        print("setting ex polarization to {}".format(
                            state_curr['pol_ex']),
                              file=stderr,
                              end=' ')
                        while not state_curr['pol_ex'] == amcu.ex(
                                state_curr['pol_ex']):
                            pass
                        print('√', file=stderr)
                    # emission
                    if state_curr['pol_em'] != data_dict['pol_em']:
                        print("setting em polarization to {}".format(
                            state_curr['pol_em']),
                              file=stderr,
                              end=' ')
                        while not state_curr['pol_em'] == amcu.em(
                                state_curr['pol_em']):
                            pass
                        print('√', file=stderr)
                    amcu_free.set()
                    # wait until wheel is solidly in position before writing any data
                    time.sleep(amcu.__ser__.timeout)

                # init a log table for the state
                trails = pd.DataFrame(columns=list_head)

                while True:

                    time_cycle = time.time()

                    # DATA FIRST
                    for dq in (queue_bath, queue_pump, queue_spec, queue_amcu):
                        try:
                            data_dict.update(dq.popleft())
                            break
                        except:
                            pass
                    # add internal data
                    data_dict.update({
                        "clock": time.strftime("%Y%m%d %H%M%S"),
                        "watch": time.time() - time_start,
                        "state": state_num,
                        "T_set": state_curr["T_set"],
                        "P_set": state_curr["P_set"],
                        "msg": state_curr["msg"]
                    })

                    # SAFETY SECOND
                    # check for pressure system leak
                    if (data_dict["vol"] - vol_start) > args["vol_diff"]:
                        pump_free.clear()
                        pump.clear()
                        raise Exception("Pump has discharged > {} mL!".format(
                            args["vol_diff"]))

                    # control the air system
                    # if it's cold and air is off
                    if (data_dict['T_act'] <= data_dict["dewpt"] +
                            args["dew_tol"]) and not data_dict['air']:
                        pump_free.clear()
                        if waited: print(file=stderr)
                        print("\nturning air ON", file=stderr, end=' ')
                        while not pump.digital(0, 1):
                            pass
                        print("√", file=stderr)
                        pump_free.set()
                        data_dict['air'] = True
                    # if it's warm and the air is on
                    elif (data_dict['T_act'] >
                          (dewpt(data_dict['H_amb'], data_dict['T_amb']) +
                           args["dew_tol"])) and data_dict['air']:
                        # and air is on
                        pump_free.clear()
                        if waited: print(file=stderr)
                        print("\nturning air OFF", file=stderr, end=' ')
                        while not pump.digital(0, 0):
                            pass
                        print("√", file=stderr)
                        pump_free.set()
                        data_dict['air'] = False

                    # does the state change require equilibration?
                    vars_wait = [var for var in args["eqls"] if chg_prev[var]]
                    # if this in an empty dict, all(in_range.values()) will be true
                    in_range = {var: False for var in vars_wait}

                    # put new data into a trailing buffer
                    trails = trails.append(data_dict, ignore_index=True)
                    # cut the buffer down to the min equil time of the slowest variable
                    if vars_wait:
                        trails = trails[trails['watch'] >= (
                            trails['watch'].iloc[-1] -
                            max([min(args["eqls"][var])
                                 for var in vars_wait]))]

                    # if the fluor reading has changed, write line to logfile
                    # this check takes about 0.1 ms
                    if (trails.shape[0]
                            == 1) or (trails["intensity"].iloc[-1] !=
                                      trails["intensity"].iloc[-2]):
                        # write data to file
                        hand_log.write('\t'.join(
                            [str(data_dict[col]) for col in list_head]) + '\n')
                        hand_log.flush()

                    for var in vars_wait:
                        # if variable's timeout is past
                        if (time_cycle - time_state) >= max(args["eqls"][var]):
                            in_range[var] = True
                        # else, if min equilibration has elapsed
                        elif (time_cycle - time_state) >= min(
                                args["eqls"][var]):
                            #print("{}: {}\r".format(var, data_dict[var]), end='')
                            # see if the trace of the variable is in range
                            trace = trails[trails['watch'] >= (
                                trails['watch'].iloc[-1] -
                                min(args["eqls"][var]))][
                                    setp2meas[var]].tolist()
                            #print(trace)
                            # and green- or redlight the variable as appropriate
                            in_range[var] = ((max(trace) <
                                              (state_curr[var] +
                                               args["tols"][setp2meas[var]]))
                                             and
                                             (min(trace) >
                                              (state_curr[var] -
                                               args["tols"][setp2meas[var]])))

                    # if all equilibrations have cleared
                    if all(in_range.values()):

                        if waited: print(file=stderr)  # newline
                        waited = False

                        # open the shutter
                        if (not readings) and args["auto_shut"] and len(
                                vars_wait) and not sat:
                            spec_free.clear()
                            while not spec.shutter(True):
                                pass
                            spec_free.set()
                            time_open = time.time()

                        # let the dye relax after a long dark period (temp xsition)
                        if ("T_set" not in vars_wait) or (
                            (time.time() - args["shut_sit"]) >= time_open):
                            # take some readings
                            if (trails.shape[0]
                                    == 1) or (trails["intensity"].iloc[-1] !=
                                              trails["intensity"].iloc[-2]):
                                if readings:
                                    print("reading {}: {} AU \r".format(
                                        readings,
                                        trails['intensity'].iloc[-1]),
                                          end='',
                                          file=stderr)
                                readings += 1
                            # break out of loop to next state
                            if (readings > args["n_read"]):
                                if args["auto_shut"] and any([
                                        chg_next[var]
                                        for var in args["eqls"].keys()
                                ]):
                                    spec_free.clear()
                                    while not spec.shutter(False):
                                        pass
                                    spec_free.set()
                                print(file=stderr)
                                break
                        else:
                            print("dye relaxed for {}/{} s\r".format(
                                round(time.time() - time_open),
                                args["shut_sit"]),
                                  end='',
                                  file=stderr)
                            sat = True
                            # gotta get a newline in here somewhere!
                    else:
                        # what are we waiting for?
                        print("waiting {} s to get {} s of stability\r".format(
                            round(time.time() - time_state),
                            max([min(args["eqls"][var])
                                 for var in vars_wait])),
                              end='',
                              file=stderr)
                        waited = True

                    # prescribed sleep
                    try:
                        time.sleep((args["cycle_time"] / 1000) -
                                   (time.time() - (time_cycle)))
                    except:
                        pass

            # shut down when done
            pump_free.clear()
            pump.digital(0, 0)
            pump.pause()
            pump.disconnect()
            bath_free.clear()
            bath.on(False)
            bath.disconnect()
            spec_free.clear()
            spec.shutter(True)
            spec.disconnect()
            amcu_free.clear()
            amcu.__ser__.send_break(duration=amcu.__ser__.timeout + 0.1)
            time.sleep(amcu.__ser__.timeout + 0.1)
            amcu.lamp(False)
            amcu.ex('0')
            amcu.em('0')
            sys.exit(0)

        except:
            pump_free.clear()
            pump.pause()
            pump.digital(0, 0)  # turn the air off!
            spec_free.clear()
            spec.ack()
            spec.shutter(False)
            amcu_free.clear()
            amcu.__ser__.send_break(duration=amcu.__ser__.timeout + 0.1)
            time.sleep(amcu.__ser__.timeout + 0.1)
            amcu.lamp(False)
            amcu.ex('0')
            amcu.em('0')
            traceback.print_exc()
        self.__ser__.flush()
        return float(self.__ser__.readline().decode().strip())


# open output file
with open(file_log, 'w') as hand_log:

    # write header
    hand_log.write("\t".join(["clock", "watch", "T_int", "T_ext", "T_ref"]) +
                   '\n')
    hand_log.flush()

    # now we're opening serial connections, which need to be closed cleanly on exit
    try:
        # init water bath
        bath = isotemp.IsotempController(port="/dev/cu.usbserial-AL01M1X9")
        #nist = QTIProbe(port="/dev/cu.usbmodem606318311")
        nist = QTIProbe(port="/dev/cu.usbmodem1421301")

        ## run experiment

        # start experiment timer (i.e. stopwatch)
        time_start = time.time()

        # start circulator
        while not bath.on():
            bath.on(True)

        # set precision
        while not bath.temp_prec(2):
            pass
Beispiel #5
0
temps = np.arange(*[
    float(x) for x in (config["temps"]["min"], config["temps"]["max"],
                       config["temps"]["step"])
]).tolist()
press = np.arange(*[
    float(x) for x in (config["press"]["min"], config["press"]["max"],
                       config["press"]["step"])
]).tolist()

# open output data file
with open(config["files"]["data"], 'x') as file_data:

    # now we're opening serial connections, which need to be closed cleanly on exit
    try:
        # init water bath
        bath = isotemp.IsotempController(port=config["ports"]["bath"])

        # columns in the data table
        cols = ("clock", "watch", "temp_set", "temp_act")
        # write header
        file_data.write('\t'.join(cols) + '\n')
        # start experiment timer (i.e. stopwatch)
        time_start = time.time()

        ## run experiment
        # start circulator
        while not bath.on():
            print("starting bath")
            bath.on(True)
        for temp in temps:
            # set temp persistently
Beispiel #6
0
def main(args, stdin, stdout, stderr, aux=None):
    "Run a temperature/parameter scan and store output to a file. Aux is query method for an auxiliary sensor."
    
    # if args are passed as namespace, convert it to dict
    try:
        args = vars(args)
    except:
        pass
    
    if not stdin.isatty():
        # if a state table is passed on stdin, read it
        print("reading states from stdin", file=stderr)
        states = pd.read_csv(stdin, sep='\t')
    else:
        # generate state table from args
        ranges = {
            "T_set"  : np.arange(*args['range_T_set']).tolist(),
            "Cp" : np.arange(*args['range_Cp']).tolist(),
            "Ci" : np.arange(*args['range_Ci']).tolist(),
            "Cd" : np.arange(*args['range_Cd']).tolist(),
            "Hp" : np.arange(*args['range_Hp']).tolist(),
            "Hi" : np.arange(*args['range_Hi']).tolist(),
            "Hd" : np.arange(*args['range_Hd']).tolist()
        }
       
        # calculate cartesian product of these ranges
        states = pd.DataFrame(list(product_dict(**ranges)))
        # sort for efficient transitions
        # parameters on the right change faster
        #NTS 20200714 .iloc[::-1] reversal is #temporary, idk why ascending= doesn't work!
        states = states.sort_values(by=args['scan_rank'], ascending=args['scan_asc']).reset_index(drop=True).iloc[::-1]
        # print the generated table to stdout for records
        states.to_csv(stdout, sep='\t')
    
    ## run the experiment
    
    # open output file, do not overwrite!
    with open(args['file_log'], 'x') as hand_log:
    
        # compose and write header
        list_head = ["clock", "watch", "T_int", "T_ext"] + list(ranges.keys())
        # insert T_aux column if aux provided
        if aux:
            list_head.insert(4, "T_aux")
        line_head = "\t".join(list_head)
        hand_log.write(line_head + '\n')
        hand_log.flush()
        
        # now we're opening serial connections, which need to be closed cleanly on exit
        try:
            # init water bath
            bath = isotemp.IsotempController(args['port_isotemp'])
            
            ## run experiment
            
            # start experiment timer (i.e. stopwatch)
            time_start = time.time()
            
            # start circulator
            while not bath.on():
                bath.on(True)
                
            # set precision
            while not bath.temp_prec(2):
                pass
            
            # iterate over test states
            for state in states.itertuples():
            
                oscs = 0
                state_start = time.time()
                
                print("state {}/{}:".format(state[0], states.shape[0]), file=stderr)
                print(state[1:], file=stderr)
                
                # set temp persistently
                while not isclose(bath.temp_set(), state._asdict()['T_set']):
                    print("setpoint to {}".format(state._asdict()['T_set']), file=stderr)
                    bath.temp_set(state._asdict()['T_set'])
                    
                for drive in ('C', 'H'):
                    print("{} to PID {}".format(drive, [state._asdict()[drive + 'p'], state._asdict()[drive + 'i'], state._asdict()[drive + 'd']]), file=stderr)
                    while not all(bath.pid(drive, state._asdict()[drive + 'p'], state._asdict()[drive + 'i'], state._asdict()[drive + 'd'])):
                        pass
                
                # data logging loop
                trace = [bath.temp_get_ext()] * 3
                peaks = []
                valleys = []
                while True:
                    temp_int = bath.temp_get_int()
                    temp_ext = bath.temp_get_ext()
                    
                    list_data = [
                        # date, clock time
                        time.strftime("%Y%m%d %H%M%S"),
                        # watch time
                        str(round(time.time() - time_start, 3)),
                        # temps
                        temp_int,
                        temp_ext
                    ] + list(state[1:])
                    # insert T_aux column if aux provided
                    if aux:
                        list_head.insert(4, aux())
                    line_data = '\t'.join([str(x) for x in list_data])
                    hand_log.write(line_data+'\n')
                    hand_log.flush()
                    
                    # if temp has changed by 0.1˚C, update trend
                    if abs(temp_ext - trace[-1]) > 0.1:
                        trace = trace[1:] + [temp_ext]
                    
                    # oscillation counter
                    if trace[0] < trace[1] > trace[2]:
                        peaks.append(trace[1])
                        # update trace
                        trace[2] = trace[1]
                    elif trace[0] > trace[1] < trace[2]:
                        valleys.append(trace[1])
                        # update trace
                        trace[2] = trace[1]
                        
                    oscs = min(len(peaks), len(valleys))
                    
                    # oscillation count bailout
                    if oscs >= args['oscillations']:
                        print("{} oscillations completed".format(oscs), file=stderr)
                        break
                    # time bailout
                    elif (time.time() - state_start) > args['timeout']:
                        print("args['timeout'] {} s reached after {} oscillations".format(args['timeout'], oscs), file=stderr)
                        break
                    
            # shut down when done
            bath.on(False)
            bath.disconnect()
    
        except:
            bath.disconnect()
            traceback.print_exc()