Added README and reorganized directory structure (breaks code)
[hooke.git] / hooke / plugin / generalclamp.py
diff --git a/hooke/plugin/generalclamp.py b/hooke/plugin/generalclamp.py
new file mode 100644 (file)
index 0000000..4ac30e7
--- /dev/null
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+
+'''
+GENERALCLAMP.py
+
+Plugin regarding general force clamp measurements
+'''
+from libhooke import WX_GOOD, ClickedPoint
+import wxversion\r
+import libhookecurve as lhc
+wxversion.select(WX_GOOD)
+from wx import PostEvent\r
+
+class generalclampCommands:\r
+\r
+    def plotmanip_clamp(self, plot, current, customvalue=False):
+        '''
+        Handles some viewing options for the "force clamp" data format, depending on the state of these configuration variables:\r
+        (1) If self.config['fc_showphase'] != 0, the 'phase' data column (i.e. the 2nd) is shown in the 0th graph (else it isn't)\r
+        (2) If self.config['fc_showimposed'] != 0, the 'imposed deflection' data column (i.e. the 5th) is shown in the 1st graph (else it isn't)\r
+        (3) If self.config['fc_interesting'] == 0, the entire curve is shown in the graphs; if it has a non-zero value N, only phase N is shown.
+\r
+        NOTE - my implementation of point(3) feels quite awkward - someone smarter than me plz polish that!\r
+\r
+        '''
+        
+        #not a fclamp curve...
+        if current.curve.experiment != 'clamp':
+            return plot\r
+\r
+        if self.config['fc_interesting'] != 0 and plot.destination==0:\r
+            lower = int((self.config['fc_interesting'])-1)\r
+            upper = int((self.config['fc_interesting'])+1)\r
+            trim = current.curve.trimindexes()[lower:upper]\r
+            newtime = []\r
+            newzpiezo = []\r
+            newphase = []\r
+            for x in range(trim[0],trim[1]):\r
+                newtime.append(self.plots[0].vectors[0][0][x])\r
+                newzpiezo.append(self.plots[0].vectors[0][1][x])\r
+                newphase.append(self.plots[0].vectors[1][1][x])\r
+            self.plots[0].vectors[0][0] = newtime\r
+            self.plots[0].vectors[0][1] = newzpiezo\r
+            self.plots[0].vectors[1][0] = newtime\r
+            self.plots[0].vectors[1][1] = newphase\r
+\r
+        if self.config['fc_interesting'] != 0 and plot.destination==1:\r
+            lower = int((self.config['fc_interesting'])-1)\r
+            upper = int((self.config['fc_interesting'])+1)\r
+            trim = current.curve.trimindexes()[lower:upper]\r
+            newtime = []\r
+            newdefl = []\r
+            newimposed = []\r
+            for x in range(trim[0],trim[1]):\r
+                newtime.append(self.plots[1].vectors[0][0][x])\r
+                newdefl.append(self.plots[1].vectors[0][1][x])\r
+                newimposed.append(self.plots[1].vectors[1][1][x])\r
+            self.plots[1].vectors[0][0] = newtime\r
+            self.plots[1].vectors[0][1] = newdefl\r
+            self.plots[1].vectors[1][0] = newtime\r
+            self.plots[1].vectors[1][1] = newimposed            \r
+                        \r
+        if self.config['fc_showphase'] == 0 and plot.destination==0:\r
+            self.plots[0].remove_set(1)\r
+            \r
+        if self.config['fc_showimposed'] == 0 and plot.destination==1:\r
+            self.plots[1].remove_set(1)\r
+                         \r
+        return plot
+      
+    def do_time(self,args):
+        '''
+        Measures the time difference (in seconds) between two points
+        Implemented only for force clamp
+        ----
+        Syntax: time
+        '''
+        if self.current.curve.experiment == 'clamp':\r
+            time=self._delta(set=0)[0]\r
+            print str(time*1000)+' ms'
+        else:
+            print 'This command makes no sense for a non-force clamp experiment.'
+            
+    def do_zpiezo(self,args):
+        '''
+        Measures the zpiezo difference (in nm) between two points
+        Implemented only for force clamp
+        ----
+        Syntax: zpiezo
+        '''
+        if self.current.curve.experiment == 'clamp':
+            zpiezo=self._delta(set=0)[2]\r
+            print str(zpiezo*(10**9))+' nm'
+        else:
+            print 'This command makes no sense for a non-force clamp experiment.'
+            
+    def do_defl(self,args):
+        '''
+        Measures the deflection difference (in nm) between two points
+        Implemented only for force clamp
+        NOTE: It makes sense only on the time VS defl plot; it is still not masked for the other plot...
+        -----
+        Syntax: defl
+        '''
+        if self.current.curve.experiment == 'clamp':
+            print "Warning - don't use on the zpiezo plot!"
+            defl=self._delta(set=1)[2]
+            print str(defl*(10**12))+' pN'
+        else:
+            print 'This command makes no sense for a non-force clamp experiment.'
+            
+    def do_step(self,args):
+        '''
+        Measures the length and time duration of a time-Z step
+        -----
+        Syntax: step
+        '''
+        if self.current.curve.experiment == 'clamp':
+            print 'Click three points in this fashion:'
+            print ' (0)-------(1)'
+            print '           |'
+            print '           |'
+            print '           (2)----------'
+            points=self._measure_N_points(N=3,whatset=0)
+            dz=abs(points[2].graph_coords[1]-points[1].graph_coords[1])*(10e+8)
+            dt=abs(points[1].graph_coords[0]-points[0].graph_coords[0])
+            print 'dZ: ',dz,' nm'
+            print 'dT: ',dt,' s'
+            
+        else:
+            print 'This command makes no sense for a non-force clamp experiment.'\r
+\r
+    def do_fcfilt(self,args):\r
+        '''\r
+        Filters out featureless force clamp curves of the current playlist.\r
+        It's very similar to 'flatfilt' for velocity clamp curves.\r
+        Creates a new playlist only containing non-empty curves.\r
+\r
+        WARNING - Only works if you set an appropriate fc_interesting config variable!\r
+        WARNING - arguments are NOT optional at the moment!\r
+\r
+        Syntax: fcfilt maxretraction(nm) mindeviation (pN)\r
+\r
+        Suggested values for an (i27)8 experiment with our setup are 200nm and 10-15 pN\r
+        '''\r
+\r
+        if self.config['fc_interesting'] == 0:\r
+            print 'You must specify the phase of interest (using set fc_interesing X) prior to running fcfilt!'\r
+            return\r
+        \r
+        maxretraction=0\r
+        threshold=0\r
+        args=args.split(' ')\r
+        if len(args)==2:\r
+            maxretraction=int(args[0])\r
+            threshold=int(args[1])\r
+        else:\r
+            print 'Arguments are not optional for fcfilt. You should pass two numbers:'\r
+            print '(1) the maximum plausible piezo retraction in NANOMETERS (e.g. the length of the protein)'\r
+            print "(2) the threshold, in PICONEWTONS. If signal deviates from imposed more than this, it's an event"\r
+            return\r
+        \r
+\r
+        print 'Processing playlist... go get yourself a cup of coffee.'\r
+        notflat_list=[]\r
+\r
+        c=0\r
+\r
+        for item in self.current_list:\r
+            c+=1\r
+            try:\r
+                notflat=self.has_stuff(item,maxretraction,threshold)\r
+                print 'Curve',item.path,'is',c,'of',len(self.current_list),'--->Has Stuff =',notflat\r
+            except:\r
+                notflat=False\r
+                print 'Curve',item.path,'is',c,'of',len(self.current_list),'--->could not be processed'\r
+            if notflat:\r
+                item.features=notflat\r
+                item.curve=None\r
+                notflat_list.append(item)\r
+\r
+        if len(notflat_list)==0:\r
+            print 'Nothing interesting here. Reconsider either your filtering criteria or your experimental data'\r
+            return\r
+        else:\r
+            print 'Found',len(notflat_list),'potentially interesting curves.'\r
+            print 'Regenerating Playlist...'\r
+            self.pointer=0\r
+            self.current_list=notflat_list\r
+            self.current=self.current_list[self.pointer]\r
+            self.do_plot(0)\r
+\r
+    def has_stuff(self,item,maxretraction,threshold):\r
+        '''\r
+        Decides whether a curve has some features in the interesting phase.\r
+        Algorithm:\r
+            - clip the interesting phase portion of the curve.\r
+            - discard the first 20 milliseconds (this is due to a quirk of our hardware).\r
+            - look at the zpiezo plot and note down when (if) retratcs more than [maxretraction] nm away from the first point.\r
+            - clip off any data after this point, with an excess of 100 points (again, an hardware quirk)\r
+            - if the remainder is less than 100 points, ditch the curve.\r
+            - now look at the deflection plot and check if there are points more than [threshold] pN over the 'flat zone'.\r
+            - if you find such points, bingo!            \r
+        '''\r
+\r
+        item.identify(self.drivers)\r
+   \r
+        lower = int((self.config['fc_interesting'])-1)\r
+        upper = int((self.config['fc_interesting'])+1)\r
+        trim_idxs = item.curve.trimindexes()[lower:upper]\r
+        lo=trim_idxs[0]+20                                                  #clipping the first 20 points off...\r
+        hi=trim_idxs[1]\r
+        trimmed_zpiezo=item.curve.default_plots()[0].vectors[0][1][lo:hi]\r
+        trimmed_defl=item.curve.default_plots()[1].vectors[0][1][lo:hi]\r
+        trimmed_imposed=item.curve.default_plots()[1].vectors[1][1][lo:hi]\r
+        imposed=trimmed_imposed[21]                                         #just to match the 20-pts clipping...\r
+        \r
+        item.curve.close_all()\r
+        del item.curve\r
+        del item\r
+\r
+        starting_z=trimmed_zpiezo[0]\r
+        plausible=starting_z-(maxretraction*1e-9)\r
+        det_trim=0\r
+        while trimmed_zpiezo[det_trim]>plausible:\r
+            det_trim+=1\r
+            if det_trim >= len(trimmed_zpiezo):                              #breaking cycles makes me shiver...\r
+                det_trim=len(trimmed_zpiezo)                                 #but I cannot think of anything better now.\r
+                break\r
+        further_trim=det_trim-100\r
+        if further_trim<100:\r
+            return False\r
+        trimmed_defl=trimmed_defl[:further_trim]\r
+\r
+        trimmed_defl.sort()\r
+        ninetypercent=int(0.9*len(trimmed_defl))\r
+        j=0\r
+        sum=0\r
+        for j in trimmed_defl[:ninetypercent]:\r
+            sum+=j\r
+        avg=float(sum/ninetypercent)\r
+        sweetspot=float(avg+(threshold*1e-12))\r
+        if trimmed_defl[-1]>sweetspot:\r
+            flag=True\r
+        else:\r
+            flag=False\r
+\r
+        del trimmed_defl,trimmed_zpiezo,trimmed_imposed            \r
+\r
+        return flag            \r
+        
\ No newline at end of file