-TEXT_VERBOSE = False
-PYLAB_INTERACTIVE_VERBOSE = True
-BASE_FIG_NUM = 40
-LOG_DATA = True
-LOG_DIR = '$DEFAULT$/unfold'
-
-import stepper
-import piezo.z_piezo as z_piezo
-import piezo.z_piezo_utils as z_piezo_utils
-import piezo.x_piezo as x_piezo
-import temperature
-import threading
-import time
-import os, os.path
-from numpy import arange, sin, pi, isnan
-import data_logger
-
-if PYLAB_INTERACTIVE_VERBOSE :
- from pylab import figure, plot, title, legend, hold, subplot, draw
- import time # for timestamping lines on plots
-
-EFILE = file(os.path.expanduser('~/rsrch/debug.unfold'), 'a')
-
-class ExceptionTooClose (Exception) :
- """
- The piezo is too close to the surface,
- an unfolding would extend past pzMin
- """
-class ExceptionTooFar (Exception) :
- """
- The piezo has reached pzMax without
- reaching the desired deflection setpoint.
- """
-
-def plot_dict(d, label) :
- try :
- plot(d["Z piezo output"], d["Deflection input"], '.',label=label)
- except KeyError, string:
- print d.keys()
- raise KeyError, string
-
-class unfold_data_log (data_logger.data_log) :
- def save(self, unfold, timestamp, CTemp, VSetpoint, nmBindPos,
- sBindTime,
- nmDist, nmStep, ppStep, nmPsRate, freq,
- approach_data, unfold_data) :
- filepath,timestamp = self.get_filename(timestamp)
- # save the unfolding data
- dataFile = open(filepath, "w")
- for i in range(0, len(unfold_data["Z piezo output"])) :
- dataFile.write("%d\t%d\t%d\n" % (unfold_data["Z piezo output"][i],
- unfold_data["Deflection input"][i],
- unfold_data["Z piezo input"][i]))
- dataFile.close()
- dataFile = open(filepath+"_approach", "w")
- for i in range(0, len(approach_data["Z piezo output"])) :
- dataFile.write("%d\t%d\n" % (approach_data["Z piezo output"][i],
- approach_data["Deflection input"][i]))
- dataFile.close()
-
- # save parameters
- paramFile = open(filepath+"_param", "w")
- paramFile.write("Environment\n")
- paramFile.write("Time:\t"+timestamp+"\n")
- if CTemp != None :
- paramFile.write("Temperature (C):\t"+str(CTemp)+"\n")
-
- paramFile.write("\nPiezo parameters\n")
- paramFile.write("Z piezo sensitivity (nm/Vp):\t"+str(unfold.zp.sensitivity)+"\n")
- paramFile.write("Z piezo gain (Vp/Vo):\t"+str(unfold.zp.gain)+"\n")
- paramFile.write("X piezo sensitivity (nm/Vp):\t"+str(unfold.xp.xpSensitivity)+"\n")
- paramFile.write("X piezo gain (Vp/Vo):\t"+str(unfold.xp.gain)+"\n")
- paramFile.write("X piezo position (nm):\t"+str(unfold.xp.out2nm(unfold.xp.curPos()))+"\n")
-
- paramFile.write("\nApproach parameters\n")
- paramFile.write("Setpoint (V):\t"+str(VSetpoint)+"\n")
- paramFile.write("Bind pos (nm):\t"+str(nmBindPos)+"\n")
-
- paramFile.write("\nBinding parameters\n")
- paramFile.write("Bind time (s):\t"+str(sBindTime)+"\n")
-
- paramFile.write("\nUnfolding parameters\n")
- paramFile.write("Distance (nm):\t"+str(nmDist)+"\n")
- paramFile.write("Step size (nm):\t"+str(nmStep)+"\n")
- paramFile.write("Points per step:\t"+str(ppStep)+"\n")
- paramFile.write("Unfold rate (nm/s):\t"+str(nmPsRate)+"\n")
- paramFile.write("Sample rate (Hz):\t"+str(freq)+"\n")
-
- paramFile.write("\nData fields:\tZ_piezo_out Deflection_in Z_piezo_in\n")
- paramFile.close()
-
- return timestamp
-
-class unfold :
- def __init__(self, controlTemp=True, dataDirectory=LOG_DIR) :
- self.step = stepper.stepper_obj()
- self.zp = z_piezo.z_piezo()
- self.xp = x_piezo.x_piezo()
- #self.zp = z_piezo.z_piezo(zp_chan = '/Dev1/ao0',
- # zp_mon_chan = '/Dev1/ai1',
- # def_chan = '/Dev1/ai0')
- #self.xp = x_piezo.x_piezo(xp_chan = '/Dev1/ao1')
- self.zp.jumpToPos(self.zp.pos_nm2out(0))
- self.xp.jumpToPos(self.xp.nm2out(0))
- if controlTemp == True :
- self.T = temperature.tempController(maxCurrent=1.0)
- else: self.T = None
- self.log = unfold_data_log(dataDirectory, log_name="unfold")
- def unfold(self, setpoint=None, rel_setpoint=1.0, sBindTime = 10.0,
- nmDist=600, nmStep=0.05, ppStep=1, nmPsRate=1000,
- dataDirectory=LOG_DIR, fileID=None) :
- while True :
- try :
- data = self.unfold_cycle(setpoint, rel_setpoint, sBindTime,
- nmDist, nmStep, ppStep, nmPsRate,
- dataDirectory, fileID)
- break
- except ExceptionTooFar :
- EFILE.write('caught ExceptionTooFar\n'); EFILE.flush()
- try : # try for a useful surface distance...
- if setpoint == None : # HACK! redundant!
- assert rel_setpoint != None, "must have some sort of setpoint"
- setpoint = self.curDef() + rel_setpoint
- print "setpoint = %g" % setpoint
- EFILE.write('attempt getSurfPos\n'); EFILE.flush()
- surfPos = self.getSurfPos(setpoint)
- EFILE.write('getSurfPos succeeded\n'); EFILE.flush()
- print "Too far (surface at %g nm), stepping closer" % surfPos
- except z_piezo_utils.poorFit, string : # ... oh well, print what we know
- EFILE.write('getSurfPos failed\n'); EFILE.flush()
- print "Too far, stepping closer"
- print "(Fit failed with: %s)" % string
- EFILE.write('zero Z piezo\n'); EFILE.flush()
- self.zp.jumpToPos(self.zp.pos_nm2out(0))
- EFILE.write('step closer\n'); EFILE.flush()
- self.stepCloser()
- EFILE.write('step closer successful\n'); EFILE.flush()
- except ExceptionTooClose :
- EFILE.write('caught ExceptionTooFar\n'); EFILE.flush()
- try : # try for a useful surface distance...
- if setpoint == None : # !HACK !redundant!
- assert rel_setpoint != None, "must have some sort of setpoint"
- setpoint = self.curDef() + rel_setpoint
- print "setpoint = %g" % setpoint
- EFILE.write('attempt getSurfPos\n'); EFILE.flush()
- surfPos = self.getSurfPos(setpoint)
- EFILE.write('getSurfPos succeeded\n'); EFILE.flush()
- print "Too close (surface at %g nm), stepping away" % surfPos
- except z_piezo_utils.poorFit, string : # ... oh well, print what we know
- EFILE.write('getSurfPos failed\n'); EFILE.flush()
- print "Too close, stepping away"
- print "(Fit failed with: %s)" % string
- EFILE.write('zero Z piezo\n'); EFILE.flush()
- self.zp.jumpToPos(self.zp.pos_nm2out(0))
- EFILE.write('step away\n'); EFILE.flush()
- self.stepAway()
- EFILE.write('step away successful\n'); EFILE.flush()
- print "Too close, stepping away"
- return data
- def stepApproach(self, setpoint) :
- cd = self.curDef()
- while cd < setpoint or isnan(cd) : # sometimes during approach, we get negative nan values
- print "deflection %g < setpoint %g. step closer" % (cd, setpoint)
- self.stepCloser()
- cd = self.curDef()
- def stepApproachRetractCurve(self, setpoint) :
- def_array = []
- step_array = []
- orig_step = self.step.get_cur_pos()
- def store_point() :
- cd = self.curDef()
- def_array.append(cd)
- step_array.append(self.step.get_cur_pos())
- return cd
- cd = store_point()
- while cd < setpoint or isnan(cd) : # sometimes during approach, we get negative nan values
- print "deflection %g < setpoint %g. step closer" % (cd, setpoint)
- self.step.step_rel(2)
- cd = store_point()
- while self.step.get_cur_pos() > orig_step :
- self.step.step_rel(-2)
- store_point()
- return {"Deflection":def_array, "Stepper position":step_array}
- def piezoApproach(self, setpoint, return_data=False) :
- startPos = self.zp.curPos()
- curVals,data = z_piezo_utils.moveToPosOrDef(self.zp,
- self.zp.zpMax,
- self.zp.def_V2in(setpoint),
- step=10,
- return_data=return_data)
- if curVals["Deflection input"] < self.zp.def_V2in(setpoint) :
- EFILE.write('Unfolding too far\n'); EFILE.flush()
- self.zp.jumpToPos(startPos)
- self.zp.updateInputs()
- if PYLAB_INTERACTIVE_VERBOSE == True :
- figure(BASE_FIG_NUM+1)
- hold(False)
- plot_dict(data, 'Approach')
- hold(True)
- title('Unfolding too far')
- EFILE.write('Raising ExceptionTooFar\n'); EFILE.flush()
- raise ExceptionTooFar
- if return_data == True :
- return (curVals, data)
- else :
- return curVals
- def bind(self, sTime=10.0) :
- time.sleep(sTime)
- def unfold_pull(self, nmDist=600, nmStep=0.5, ppStep=10, nmPsRate=1000) :
- if nmStep < 0 :
- nmStep = -nmStep
- numSteps = int(nmDist/nmStep+1)
- out = [0.0]*numSteps*ppStep
- pos = self.zp.curPos()
- startPos = pos
- step = int(self.zp.pos_nm2out(nmStep)-self.zp.pos_nm2out(0))
- for i in range(0, numSteps) :
- for j in range(0, ppStep) :
- out[i*ppStep + j] = pos
- pos -= step
- # points/step * step/nm * nm/s = points/s
- freq = ppStep / nmStep * nmPsRate
- print "unfolding %d points per %d steps from %d to %d at freq of %g" % (ppStep, numSteps, startPos, pos, freq)
- return {'freq':freq, 'data':self.zp.ramp(out, freq)}
- def generate_refold_bind_pull_output(self, nmSurfPos=0,
- sBindTime=2, nmUnfoldDist=160, nmRefoldDist=145, sPauseTime=2,
- cycles=50, nmStep=0.5, ppStepUnfold=10, ppStepRefold=1,
- nmPsRateUnfold=1000) :
-
- if nmStep < 0 :##
- nmStep = -nmStep
- numSteps = int(nmDist/nmStep+1)
- out = [0.0]*numSteps*ppStep
- pos = self.zp.curPos()
- startPos = pos
- step = int(self.zp.pos_nm2out(nmStep)-self.zp.pos_nm2out(0))
- for i in range(0, numSteps) :
- for j in range(0, ppStep) :
- out[i*ppStep + j] = pos
- pos -= step
- # points/step * step/nm * nm/s = points/s
- freq = ppStep / nmStep * nmPsRate
- print "unfolding %d points per %d steps from %d to %d at freq of %g" % (ppStep, numSteps, startPos, pos, freq)
- return {'freq':freq, 'data':self.zp.ramp(out, freq)}
- def unfold_cycle(self, setpoint=None, rel_setpoint=1.0, sBindTime = 10.0,
- nmDist=600, nmStep=0.5, ppStep=10, nmPsRate=1000,
- dataDirectory=LOG_DIR, fileID=None) :
- if setpoint == None :
- assert rel_setpoint != None, "must have some sort of setpoint"
- setpoint = self.curDef() + rel_setpoint
- print "setpoint = %g" % setpoint
- timestamp = time.strftime("%Y%m%d%H%M%S")
- temp = None
- if self.T != None : temp = self.T.getTemp()
- print "approaching"
- startPos = self.zp.curPos()
- curVals,approach_data = self.piezoApproach(setpoint, return_data=True)
- bindPos = curVals['Z piezo output'] # in output units
- finalPos = bindPos - (self.zp.pos_nm2out(nmDist)-self.zp.pos_nm2out(0))
- try :
- self.zp._check_range(finalPos)
- except z_piezo.outOfRange :
- self.zp.jumpToPos(startPos)
- self.zp.updateInputs()
- if PYLAB_INTERACTIVE_VERBOSE == True :
- figure(BASE_FIG_NUM+1)
- hold(False)
- plot_dict(approach_data, 'Approach')
- hold(True)
- title('Unfolding too close')
- raise ExceptionTooClose
- print "binding for %.3f seconds" % sBindTime
- self.bind(sBindTime)
- out = self.unfold_pull(nmDist, nmStep, ppStep, nmPsRate)
-
- if PYLAB_INTERACTIVE_VERBOSE == True :
- figure(BASE_FIG_NUM)
- hold(False)
- plot_dict(approach_data, 'Approach')
- hold(True)
- plot_dict(out['data'], 'Unfold')
- legend(loc='best')
- title('Unfolding')
- draw()
-
- if LOG_DATA == True:
- print "saving"
- timestamp = self.log.save(self, timestamp, temp, setpoint,
- int(self.zp.pos_out2nm(bindPos)), # don't need lots of precision...
- sBindTime,
- nmDist, nmStep, ppStep, nmPsRate, out['freq'],
- approach_data, out['data'])
- return out
- def getSurfPos(self, setpoint=2, textVerbose=False, plotVerbose=False) :
- return self.zp.pos_out2nm(z_piezo_utils.getSurfPos(self.zp, self.zp.def_V2in(setpoint), textVerbose, plotVerbose))
- def curDef(self) :
- self.zp.updateInputs()
- return self.zp.def_in2V(self.zp.curDef())
- def stepCloser(self) :
- "Backlash-robust stepping"
- self.step.step_rel(2) # two half-steps in full step mode
- def stepAway(self) :
- "Backlash-robust stepping"
- self.step.step_rel(-120)
- self.step.step_rel(110) # HACK, should come back 118
- def xpWander(self) :
- self.xp.wander()
-
-def _test_unfold(controlTemp=True) :
- print "Test unfold"
- u = unfold(controlTemp=controlTemp)
- u.unfold(setpoint=0.5, sBindTime=1.0)
- del u
- print "unfold successful\n"
-
-
-class unfold_expt :
- def __init__(self, controlTemp=True) :
- self.u = unfold(controlTemp=controlTemp)
- self.runUnfolds = False
- self.bgRun = None
- self.getExternalTempSetpoint = None
- if self.u.T != None :
- self.tempSetpoint = self.u.T.getTemp()
- self.getExternalDeflectionSetpoint = None
- self.deflectionSetpoint = 0.5
- self.getExternalBindTime = None
- self.bindTime = 1.0
- self.getExternalnmDist = None
- self.nmDist = 600.0
- self.getExternalnmStep = None
- self.nmStep = 0.5
- self.getExternalppStep = None
- self.ppStep = 10
- self.getExternalnmPsRate = None
- self.nmPsRate = 1000.0
- self.getExternalDataDirectory = None
- self.dataDirectory = LOG_DIR
- self.plotData = None
- self.i=0
- self.showUnfoldingIndex = None
- def run(self) :
- print "running"
- if self.bgRun != None :
- raise Exception, "Can't run two backgrounds simultaneously"
- self.runUnfolds = True
- print "unfolding in the background"
- self.bgRun = bg_run( self._run)
- def stepApproach(self) :
- print "approaching"
- if self.getExternalDeflectionSetpoint != None :
- self.deflectionSetpoint = self.getExternalDeflectionSetpoint()
- self.u.stepApproach(self.deflectionSetpoint)
- def _run(self) :
- print "starting unfold loop"
- while self.runUnfolds == True :
- print "get unfold parameters"
- if self.u.T != None and self.getExternalTempSetpoint != None :
- setpoint = self.getExternalTempSetpoint()
- if setpoint != self.tempSetpoint :
- self.u.T.setTemp(setpoint)
- self.tempSetpoint = setpoint
- if self.getExternalDeflectionSetpoint != None :
- self.deflectionSetpoint = self.getExternalDeflectionSetpoint()
- if self.getExternalBindTime != None :
- self.bindTime = self.getExternalBindTime()
- if self.getExternalnmDist != None :
- self.nmDist = self.getExternalnmDist()
- if self.getExternalnmStep != None :
- self.nmStep = self.getExternalnmStep()
- if self.getExternalppStep != None :
- self.ppStep = self.getExternalppStep()
- if self.getExternalnmPsRate != None :
- self.nmPsRate = self.getExternalnmPsRate()
- if self.getExternalDataDirectory != None :
- self.dataDirectory = self.getExternalDataDirectory()
- print "run unfold"
- #print "setpoint ", self.deflectionSetpoint
- #print "bind time ", self.bindTime
- #print "nmDist ", self.nmDist
- #print "nmStep ", self.nmStep
- #print "ppStep ", self.ppStep
- #print "nmPsRate ", self.nmPsRate
- #print "fileID ", self.i
- #print "dataDir ", self.dataDirectory
- data = self.u.unfold(setpoint=self.deflectionSetpoint,
- sBindTime=self.bindTime,
- nmDist=self.nmDist,
- nmStep=self.nmStep,
- ppStep=self.ppStep,
- nmPsRate=self.nmPsRate,
- fileID=str(self.i),
- dataDirectory=self.dataDirectory)
- print "plot data"
- if self.plotData != None :
- self.plotData(data["data"]["Z piezo output"],
- data["data"]["Deflection input"])
- if self.showUnfoldingIndex != None :
- self.showUnfoldingIndex(self.i)
- self.i += 1
- return False
- def stop(self) :
- if self.bgRun != None :
- self.runUnfolds = False
- self.bgRun.wait()
- self.bgRun = None
-
-def _test_unfold_expt(controlTemp=True) :
- print "Test unfold_expt"
- u_expt = unfold.unfold_expt(controlTemp=controlTemp)
- u_expt.run()
- time.sleep(100)
- u_expt.stop()
- print "unfold_expt working\n"
-
-class bg_run :
- def __init__(self, function, args=None, finishCallback=None, interruptCallback=None) :
- print "init bg"
- def tempFn() :
- print "temp fn started"
- complete = False
- if args == None :
- complete = function()
- else :
- complete = function(args)
- print "temp Fn finished"
- if finishCallback != None and complete == True :
- finishCallback()
- elif interruptCallback != None and complete == False :
- interruptCallback()
- print "tempFn defined"
- self.thd = threading.Thread(target=tempFn, name="Background Fn")
- print "starting thread"
- self.thd.start()
- print "thread started"
- def wait(self) :
- print "joining thread"
- self.thd.join()
- print "thread joined"
-
-def _test_bg_run() :
- print "Test bg_run"
- print "test without args or callbacks"
- def say_hello() :
- for i in range(10) :
- print "Hello"
- time.sleep(0.1)
- return True
- bg = unfold.bg_run(say_hello)
- time.sleep(.2)
- print "The main thread's still going"
- bg.wait()
- print "The background process is done"
-
- print "test without args, but with callbacks"
- def inter() :
- print "I was interrupted"
- def fin() :
- print "I'm finished"
- bg = unfold.bg_run(say_hello, finishCallback=fin, interruptCallback=inter)
- time.sleep(.2)
- print "The main thread's still going"
- bg.wait()
- print "The background process is done"
-
- print "test with args and callbacks"
- del say_hello
- def say_hello(stop=[False]) :
- for i in range(10) :
- if stop[0] == True : return False
- print "Hello"
- time.sleep(0.1)
- return True
- stop = [False]
- bg = unfold.bg_run(say_hello, args=stop, finishCallback=fin, interruptCallback=inter)
- time.sleep(.2)
- print "The main thread's still going, stop it"
- stop[0] = True
- bg.wait()
- del bg
- del stop
- del say_hello
- del inter
- del fin
- print "The background process is done"
-
- print "bg_run working\n"
-
-def loop_rates(u, rates, num_loops=10, die_file=None, song=None, **kwargs):
- """
- loop_rates(u, rates, num_loops=10, die_file=None, song=None, **kwargs)
-
- Constant speed unfolding using the unfold instance u for num_loops
- through the series of rates listed in rates. You may set die_file
- to a path, loop_rates() will check that location before each
- unfolding attempt, and cleanly stop unfolding if the file exists.
- **kwargs are passed on to u.unfold(), so a full call might look like
-
- loop_rates(u, rates=[20,200,2e3], num_loops=5, die_file='~/die', song='~wking/Music/system/towerclo.wav', rel_setpoint=1, nmDist=800, sBindTime=2)
- """
- if die_file != None:
- die_file = os.path.expanduser(die_file)
- if song != None:
- song = os.path.expanduser(song)
- for i in range(num_loops):
- for nmPsRate in rates:
- if die_file != None and os.path.exists(die_file):
- return None
- u.unfold(nmPsRate=nmPsRate, **kwargs)
- u.xpWander()
- if song != None:
- os.system("aplay '%s'" % song)
-
-def test() :
- _test_unfold(controlTemp=False)
- _test_bg_run()
- _test_unfold_expt(controlTemp=False)
-
-if __name__ == "__main__" :
- test()
+#!/usr/bin/env python
+#
+# Copyright (C) 2011 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of unfold_protein.
+#
+# Unfold_protein is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+#
+# Unfold_protein is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with unfold_protein. If not, see
+# <http://www.gnu.org/licenses/>.
+
+"""Run a mechanical protein unfolding experiment."""
+
+import os
+import os.path
+
+from unfold_protein import __version__ as version
+from unfold_protein.afm import get_afm
+from unfold_protein.unfolder import Unfolder
+from unfold_protein.scan import UnfoldScanner
+import unfold_protein.config as _config
+
+
+if __name__ == '__main__':
+ from argparse import ArgumentParser
+
+ parser = ArgumentParser(
+ description='Play a pure tone', version=version)
+ parser.add_argument(
+ '-s', '--song',
+ help='Path to a song to play when the experiment is complete')
+
+ args = parser.parse_args()
+
+ unfold_config = _config.UnfoldCycleConfig()
+ unfold_config['temperature'] = _config.TemperatureConfig()
+ unfold_config['approach'] = _config.ApproachConfig()
+ unfold_config['unfold'] = _config.UnfoldConfig()
+ unfold_config['save'] = _config.SaveConfig()
+ scan_config = _config.ScanConfig()
+ scan_config['velocity'] = _config.VelocityScanConfig()
+ scan_config['position'] = _config.PositionScanConfig()
+
+ afm,comedi_device = get_afm(with_temperature=False)
+ unfolder = Unfolder(config=unfold_config, afm=afm)
+ scanner = UnfoldScanner(config=scan_config, unfolder=unfolder)
+ try:
+ scanner.run()
+ finally:
+ scanner.move_far_from_surface()
+ comedi_device.close()
+ if args.song:
+ song = os.path.abspath(os.path.expanduser(args.song))
+ os.system("aplay '%s'" % song)
--- /dev/null
+# Copyright (C) 2011 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of unfold_protein.
+#
+# Unfold_protein is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+#
+# Unfold_protein is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with unfold_protein. If not, see
+# <http://www.gnu.org/licenses/>.
+
+from .config import PackageConfig as _PackageConfig
+
+
+__version__ = '0.2'
+
+
+package_config = _PackageConfig(package_name=__name__)
+package_config.load_system()
--- /dev/null
+# Copyright (C) 2011 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of unfold_protein.
+#
+# Unfold_protein is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+#
+# Unfold_protein is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with unfold_protein. If not, see
+# <http://www.gnu.org/licenses/>.
+
+from pyafm.afm import AFM as _AFM
+from pycomedi.channel import AnalogChannel as _AnalogChannel
+from pycomedi.channel import DigitalChannel as _DigitalChannel
+from pycomedi.device import Device as _Device
+from pycomedi.subdevice import StreamingSubdevice as _StreamingSubdevice
+from pypiezo.afm import AFMPiezo as _AFMPiezo
+from stepper import Stepper as _Stepper
+import pycomedi.constant as _pycomedi_constant
+import pypiezo.base as _pypiezo_base
+import pypiezo.config as _pypiezo_config
+
+try:
+ from .temperature import Temperature as _Temperature
+except ImportError, _temperature_import_error:
+ _Temperature = None
+
+
+def get_afm(with_temperature=True, logger=None):
+ d = _Device('/dev/comedi0')
+ d.open()
+ s_in = d.find_subdevice_by_type(
+ _pycomedi_constant.SUBDEVICE_TYPE.ai, factory=_StreamingSubdevice)
+ s_out = d.find_subdevice_by_type(
+ _pycomedi_constant.SUBDEVICE_TYPE.ao, factory=_StreamingSubdevice)
+ z_axis_channel = s_out.channel(
+ 0, factory=_AnalogChannel,
+ aref=_pycomedi_constant.AREF.ground)
+ x_axis_channel = s_out.channel(
+ 1, factory=_AnalogChannel,
+ aref=_pycomedi_constant.AREF.ground)
+ input_channel = s_in.channel(
+ 0, factory=_AnalogChannel,
+ aref=_pycomedi_constant.AREF.diff)
+ for chan in [z_axis_channel, x_axis_channel, input_channel]:
+ chan.range = chan.find_range(
+ unit=_pycomedi_constant.UNIT.volt, min=-10, max=10)
+ z_axis_config = _pypiezo_config.AxisConfig()
+ z_axis_config.update({'gain':20, 'sensitivity':6.5e-9})
+ z_axis_channel_config = _pypiezo_config.ChannelConfig()
+ z_axis_config['channel'] = z_axis_channel_config
+ z_axis_channel_config['name'] = 'z'
+ x_axis_config = _pypiezo_config.AxisConfig()
+ x_axis_config.update({'gain':20, 'sensitivity':6.5e-9}) # TODO: GET FROM CALIBRATION
+ x_axis_channel_config = _pypiezo_config.ChannelConfig()
+ x_axis_config['channel'] = x_axis_channel_config
+ x_axis_channel_config['name'] = 'x'
+ input_channel_config = _pypiezo_config.ChannelConfig()
+ input_channel_config['name'] = 'deflection'
+ z = _pypiezo_base.PiezoAxis(
+ config=z_axis_config, axis_channel=z_axis_channel)
+ z.setup_config()
+ x = _pypiezo_base.PiezoAxis(
+ config=x_axis_config, axis_channel=x_axis_channel)
+ x.setup_config()
+ inp = _pypiezo_base.InputChannel(
+ config=input_channel_config, channel=input_channel)
+ inp.setup_config()
+ piezo = _AFMPiezo(axes=[x, z], inputs=[inp])
+
+ s_d = d.find_subdevice_by_type(
+ _pycomedi_constant.SUBDEVICE_TYPE.dio)
+ d_channels = [s_d.channel(i, factory=_DigitalChannel)
+ for i in (0, 1, 2, 3)]
+ for chan in d_channels:
+ chan.dio_config(
+ _pycomedi_constant.IO_DIRECTION.output)
+ def write(value):
+ s_d.dio_bitfield(bits=value, write_mask=2**4-1)
+ stepper = _Stepper(write=write, delay=5e-2)
+
+ if with_temperature:
+ if _Temperature is None:
+ raise _temperature_import_error
+ temperature = _Temperature()
+ else:
+ temperature = None
+
+ afm = _AFM(piezo, stepper, temperature=temperature)
+ return (afm, d)
--- /dev/null
+# Copyright (C) 2011 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of unfold_protein.
+#
+# Unfold_protein is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+#
+# Unfold_protein is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with unfold_protein. If not, see
+# <http://www.gnu.org/licenses/>.
+
+"""Define some variables to configure the package for a particular lab
+and workflow."""
+
+import os.path as _os_path
+import sys as _sys
+
+from FFT_tools import window_hann as _window_hann
+import h5config.config as _config
+import h5config.tools as _h5config_tools
+import numpy as _numpy
+from calibcant.config import TemperatureConfig
+
+
+class PackageConfig (_h5config_tools.PackageConfig):
+ "Configure `unfold_protein` module operation"
+ settings = _h5config_tools.PackageConfig.settings + [
+ _config.BooleanSetting(
+ name='matplotlib',
+ help='Plot piezo motion using `matplotlib`.',
+ default=False),
+ _config.FloatSetting(
+ name='temperature',
+ help=('Default temperature for unfolding in degrees '
+ 'Celsius.'),
+ default=22),
+ ]
+
+class ApproachConfig (_config.Config):
+ "Configure `unfold_protein` approach operation"
+ settings = [
+ _config.FloatSetting(
+ name='relative setpoint',
+ help=('Maximum relative deflection in volts to achieve the bind '
+ 'position.'),
+ default=2.0),
+ _config.FloatSetting(
+ name='velocity',
+ help='Approach velocity in meters/second.',
+ default=1e-6),
+ _config.FloatSetting(
+ name='step',
+ help='Step size in meters.',
+ default=5e-9),
+ _config.IntegerSetting(
+ name='far',
+ help=('Approximate distance in meters to move away to get "far" '
+ 'from the surface. For possible stepper adjustments while '
+ 'initially locating the surface.'),
+ default=1e-5),
+ ]
+
+class UnfoldConfig (_config.Config):
+ "Configure `unfold_protein` unfold operation"
+ settings = [
+ _config.FloatSetting(
+ name='distance',
+ help='Unfolding distance in meters.',
+ default=800e-9),
+ _config.FloatSetting(
+ name='velocity',
+ help='Unfolding velocity in meters/second.',
+ default=1e-6),
+ _config.FloatSetting(
+ name='frequency',
+ help='Sampling frequency in Hz.',
+ default=50e3),
+ ]
+
+class SaveConfig (_config.Config):
+ "Configure `unfold_protein` unfold operation"
+ settings = [
+ _config.Setting(
+ name='base directory',
+ help='Root directory for saving data.',
+ default=_os_path.expanduser('~/rsrch/data/unfold/')),
+ ]
+
+class UnfoldCycleConfig (_config.Config):
+ "Configure a full `unfold_protein` approach-bind-unfold cycle"
+ settings = [
+ _config.ConfigSetting(
+ name='temperature',
+ help='Configure the temperature measurements',
+ config_class=TemperatureConfig),
+ _config.ConfigSetting(
+ name='approach',
+ help=('Configure the approach for an approach-bind-unfold '
+ 'sequence.'),
+ config_class=ApproachConfig),
+ _config.FloatSetting(
+ name='bind time',
+ help=('Binding time in seconds for an approach-bind-unfold '
+ 'sequence.'),
+ default=3),
+ _config.ConfigSetting(
+ name='unfold',
+ help=('Configure the unfolding for an approach-bind-unfold '
+ 'sequence.'),
+ config_class=UnfoldConfig),
+ _config.ConfigSetting(
+ name='save',
+ help='Configure saving.',
+ config_class=SaveConfig),
+ ]
+
+class VelocityScanConfig (_config.Config):
+ "Configure `unfold_config` unfolding velocity scan"
+ settings = [
+ _config.FloatListSetting(
+ name='unfolding velocities',
+ help='Unfolding velocities in meters per second.',
+ default=_numpy.exp(_numpy.linspace(
+ _numpy.log(20e-9), _numpy.log(2e-6), 10))),
+ _config.IntegerSetting(
+ name='num loops',
+ help='Number of loops through the scanned velocities.',
+ default=10),
+ ]
+
+class PositionScanConfig (_config.Config):
+ "Configure `unfold_config` contact position scan"
+ settings = [
+ _config.FloatSetting(
+ name='x step',
+ help=('Distance in meters along the x axis between successive '
+ 'onfoldings.'),
+ default=5e-9),
+ _config.FloatSetting(
+ name='x max',
+ help='Maximum sampled x position in meters.',
+ default=1e-6),
+ _config.FloatSetting(
+ name='x min',
+ help='Minimum sampled x position in meters.',
+ default=-1e-6),
+ ]
+
+class ScanConfig (_config.Config):
+ "Configure a full `unfold_protein` approach-bind-unfold cycle"
+ settings = [
+ _config.ConfigSetting(
+ name='velocity',
+ help='Configure unfolding velocity scan pattern.',
+ config_class=VelocityScanConfig),
+ _config.ConfigSetting(
+ name='position',
+ help='Configure unfolding position scan pattern.',
+ config_class=PositionScanConfig),
+ ]
--- /dev/null
+# Copyright (C) 2011 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of unfold_protein.
+#
+# Unfold_protein is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+#
+# Unfold_protein is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with unfold_protein. If not, see
+# <http://www.gnu.org/licenses/>.
+
+"""Define `UnfoldScanner` for sequential unfolding experiments."""
+
+import signal as _signal
+
+from calibcant.calibrate import move_far_from_surface
+import pypiezo.base as _pypiezo_base
+
+from . import LOG as _LOG
+from .unfolder import ExceptionTooFar as _ExceptionTooFar
+from .unfolder import ExceptionTooClose as _ExceptionTooClose
+
+class UnfoldScanner (object):
+ def __init__(self, config, unfolder):
+ self.config = config
+ self.unfolder = unfolder
+ self._state = {'x direction': 1}
+
+ def run(self):
+ self._stop = False
+ _signal.signal(_signal.SIGTERM, self._handle_stop_signal)
+ for i in range(self.config['velocity']['num loops']):
+ for velocity in self.config['velocity']['unfolding velocities']:
+ if self._stop:
+ return
+ self.unfolder.config['unfold']['velocity'] = velocity
+ try:
+ self.unfolder.run()
+ except _ExceptionTooFar:
+ self.stepper_approach()
+ except _ExceptionTooClose:
+ self.move_far_from_surface()
+ self.stepper_approach()
+ else:
+ self.position_scan_step()
+
+ def _handle_stop_signal(self, signal, frame):
+ self._stop = True
+
+ def move_far_from_surface(self):
+ _LOG.info('retract with the stepper motor')
+ move_far_from_surface(
+ stepper=self.unfolder.afm.stepper,
+ distance=self.unfolder.config['approach']['far'])
+
+ def stepper_approach(self):
+ config = self.unfolder.config['approach']
+ deflection = self.unfolder.read_deflection()
+ setpoint = deflection + config['relative setpoint']
+ def_config = self.unfolder.afm.piezo.config.select_config(
+ 'inputs', 'deflection')
+ setpoint_bits = _pypiezo_base.convert_volts_to_bits(
+ def_config, setpoint)
+ self.unfolder.afm.stepper_approach(target_deflection=setpoint_bits)
+
+ def position_scan_step(self):
+ axis_name = 'x'
+ config = self.config['position']
+ axis_config = self.unfolder.afm.piezo.config.select_config(
+ 'axes', self.unfolder.afm.axis_name,
+ get_attribute=_pypiezo_base.get_axis_name
+ )
+ pos = self.unfolder.afm.piezo.last_output[axis_name]
+ pos_m = _pypiezo_base.convert_bits_to_meters(axis_config, pos)
+ next_pos_m = pos_m + self._state['x direction']*config['x step']
+ if next_pos_m > config['x max']:
+ self._state['x direction'] = -1
+ next_pos_m = pos_m + self._state['x direction']*config['x step']
+ elif next_pos_m < config['x min']:
+ self._state['x direction'] = 1
+ next_pos_m = pos_m + self._state['x direction']*config['x step']
+ next_pos = _pypiezo_base.convert_meters_to_bits(
+ axis_config, next_pos_m)
+ _LOG.info('move {} from {:g} to {:g} bits'.format(
+ axis_name, pos, next_pos))
+ self.unfolder.afm.piezo.jump(axis_name, next_pos)
--- /dev/null
+# Copyright (C) 2011 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of unfold_protein.
+#
+# Unfold_protein is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+#
+# Unfold_protein is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with unfold_protein. If not, see
+# <http://www.gnu.org/licenses/>.
+
+from pypid.backend.melcor import MelcorBackend as _TemperatureBackend
+
+from . import LOG as _LOG
+
+
+class Temperature (_TemperatureBackend):
+ def __init__(self, **kwargs):
+ _LOG.debug('setup temperature monitor')
+ super(Temperature, self).__init__(self, **kwargs)
+ self.set_max_mv(max=1) # amp
+
+ def get_temperature(self):
+ temp = self.get_pv() + 273.15 # return temperature in kelvin
+ _LOG.info('measured temperature of {:g} K'.format(temp))
+ return temp
--- /dev/null
+# Copyright (C) 2011 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of unfold_protein.
+#
+# Unfold_protein is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+#
+# Unfold_protein is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with unfold_protein. If not, see
+# <http://www.gnu.org/licenses/>.
+
+"""Define classes for carrying out an unfolding cycle with an AFM."""
+
+import email.utils as _email_utils
+import os.path as _os_path
+import time as _time
+
+import h5py as _h5py
+import pypiezo.base as _pypiezo_base
+from h5config.storage.hdf5 import HDF5_Storage as _HDF5_Storage
+from h5config.storage.hdf5 import h5_create_group as _h5_create_group
+
+from . import LOG as _LOG
+from . import package_config as _package_config
+
+try:
+ import numpy as _numpy
+ from matplotlib import pyplot as _pyplot
+ FIGURE = _pyplot.figure()
+except (ImportError, RuntimeError), _matplotlib_import_error:
+ _pyplot = None
+# from pylab import figure, plot, title, legend, hold, subplot, draw
+
+
+class ExceptionTooClose (Exception):
+ """
+ The piezo is too close to the surface.
+ """
+ pass
+
+class ExceptionTooFar (Exception):
+ """
+ The piezo is too far from the surface.
+ """
+ pass
+
+
+class Unfolder (object):
+ def __init__(self, config, afm):
+ self.config = config
+ self.afm = afm
+ self.zero_piezo()
+
+ def run(self):
+ """Approach-bind-unfold-save[-plot] cycle.
+ """
+ ret = {}
+ ret['timestamp'] = _email_utils.formatdate(localtime=True)
+ ret['temperature'] = self.afm.get_temperature()
+ ret['approach'] = self._approach()
+ self._bind()
+ ret['unfold'] = self._unfold()
+ self._save(**ret)
+ if _package_config['matplotlib']:
+ self._plot(**ret)
+ return ret
+
+ def _approach(self):
+ """Approach the surface using the piezo
+
+ Steps in until a given setpoint is reached.
+ """
+ config = self.config['approach']
+ deflection = self.read_deflection()
+ setpoint = deflection + config['relative setpoint']
+ _LOG.info('approach with setpoint = {}'.format(setpoint))
+ axis_config = self.afm.piezo.config.select_config(
+ 'axes', self.afm.axis_name,
+ get_attribute=_pypiezo_base.get_axis_name
+ )
+ def_config = self.afm.piezo.config.select_config(
+ 'inputs', 'deflection')
+ start_pos = self.afm.piezo.last_output[self.afm.axis_name]
+
+ # calculate parameters for move_to_pos_or_def from config
+ setpoint_bits = _pypiezo_base.convert_volts_to_bits(
+ def_config, setpoint)
+ mid_pos_bits = _pypiezo_base.convert_meters_to_bits(
+ axis_config, 0)
+ step_pos_bits = _pypiezo_base.convert_meters_to_bits(
+ axis_config, config['step'])
+ step_bits = step_pos_bits - mid_pos_bits
+ frequency = config['velocity'] / config['step']
+
+ # run the approach
+ data = self.afm.piezo.move_to_pos_or_def(
+ axis_name=self.afm.axis_name, deflection=setpoint_bits,
+ step=step_bits, frequency=frequency, return_data=True)
+ data['setpoint'] = setpoint
+ # check the output
+ if data['deflection'][-1] < setpoint_bits:
+ _LOG.info('unfolding too far from the surface')
+ self.afm.piezo.jump(self.afm.axis_name, start_pos)
+ #if PYLAB_INTERACTIVE_VERBOSE == True:
+ # figure(BASE_FIG_NUM+1)
+ # hold(False)
+ # plot_dict(data, 'Approach')
+ # hold(True)
+ # title('Unfolding too far')
+ _LOG.debug('raising ExceptionTooFar')
+ raise ExceptionTooFar
+ return data
+
+ def _bind(self):
+ """Wait on the surface while the protein binds."""
+ time = self.config['bind time']
+ _LOG.info('binding for {:.3f} seconds'.format(time))
+ _time.sleep(time)
+
+ def _unfold(self):
+ """Pull the bound protein, forcing unfolding events."""
+ config = self.config['unfold']
+ velocity = config['velocity']
+ _LOG.info('unfold at {:g} m/s'.format(velocity))
+ axis = self.afm.piezo.axis_by_name(self.afm.axis_name)
+ d = self.afm.piezo.channel_by_name('deflection')
+ start_pos = self.afm.piezo.last_output[self.afm.axis_name]
+ start_pos_m = _pypiezo_base.convert_bits_to_meters(axis, start_pos)
+ final_pos_m = bind_pos_m - config['distance']
+ final_pos = _pypiezo_base.convert_meters_to_bits(axis, final_pos_m)
+ dtype = afm.piezo.channel_dtype(self.afm.axis_name, direction='output')
+ num_steps = int(
+ config['distance'] / config['velocity'] * config['frequency']) + 1
+ # (m) * (s/m) * (samples/s)
+ out = _numpy.linspace(
+ start_pos, final_pos, num_steps).astype(dtype=dtype)
+ _LOG.debug(
+ 'unfolding from {:d} to {:d} in {:d} steps at {:g} Hz'.format(
+ start_pos, final_pos, num_steps, config['frequency']))
+ data = afm.piezo.ramp(
+ data=out, frequency=config['frequency'],
+ output_names=[self.afm.axis_name], input_names=['deflection'])
+ return {self.afm.axis_name:out, 'deflection':data}
+
+ def _save(self, temperature, approach, unfold, timestamp):
+ config = self.config['save']
+ time_tuple = _email_utils.parsedate(timestamp)
+ filename = _os_path.join(
+ config['base directory'],
+ '{0}-{1:02d}-{2:02d}_{3:02d}-{4:02d}-{5:02d}.h5'.format(
+ time_tuple))
+ _LOG.info('saving {}'.format(filename))
+ with _h5py.File(filename, 'a') as f:
+ storage = _HDF5_Storage()
+ config_cwg = _h5_create_group(f, 'config')
+ storage.save(config=self.config, group=config_cwg)
+ f['timestamp'] = timestamp
+ f['temperature'] = temperature
+ for k,v in approach.items():
+ f['approach/{}'.format(k)] = v
+ for k,v in unfold.items():
+ f['unfold/{}'.format(k)] = v
+
+ def _plot(self, temperature, approach, unfold, timestamp):
+ "Plot the unfolding cycle"
+ if not _matplotlib:
+ raise _matplotlib_import_error
+ FIGURE.clear()
+ # TODO...
+ #if PYLAB_INTERACTIVE_VERBOSE == True:
+ # figure(BASE_FIG_NUM)
+ # hold(False)
+ # plot_dict(approach_data, 'Approach')
+ # hold(True)
+ # plot_dict(out['data'], 'Unfold')
+ # legend(loc='best')
+ # title('Unfolding')
+ # draw()
+
+ def zero_piezo(self):
+ _LOG.info('zero piezo')
+ x_mid_pos = _pypiezo_base.convert_volts_to_bits(
+ self.afm.piezo.config.select_config(
+ 'axes', 'x', get_attribute=_pypiezo_base.get_axis_name
+ )['channel'],
+ 0)
+ z_mid_pos = _pypiezo_base.convert_volts_to_bits(
+ self.afm.piezo.config.select_config(
+ 'axes', 'z', get_attribute=_pypiezo_base.get_axis_name
+ )['channel'],
+ 0)
+ self.afm.piezo.jump('z', z_mid_pos)
+ self.afm.piezo.jump('x', x_mid_pos)
+
+ def read_deflection(self):
+ bits = self.afm.piezo.read_deflection()
+ return _pypiezo_base.convert_bits_to_volts(
+ self.afm.piezo.config.select_config('inputs', 'deflection'), bits)