Added README and reorganized directory structure (breaks code)
[hooke.git] / hooke / plugin / generalclamp.py
1 #!/usr/bin/env python
2
3 '''
4 GENERALCLAMP.py
5
6 Plugin regarding general force clamp measurements
7 '''
8 from libhooke import WX_GOOD, ClickedPoint
9 import wxversion\r
10 import libhookecurve as lhc
11 wxversion.select(WX_GOOD)
12 from wx import PostEvent\r
13
14 class generalclampCommands:\r
15 \r
16     def plotmanip_clamp(self, plot, current, customvalue=False):
17         '''
18         Handles some viewing options for the "force clamp" data format, depending on the state of these configuration variables:\r
19         (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
20         (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
21         (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.
22 \r
23         NOTE - my implementation of point(3) feels quite awkward - someone smarter than me plz polish that!\r
24 \r
25         '''
26         
27         #not a fclamp curve...
28         if current.curve.experiment != 'clamp':
29             return plot\r
30 \r
31         if self.config['fc_interesting'] != 0 and plot.destination==0:\r
32             lower = int((self.config['fc_interesting'])-1)\r
33             upper = int((self.config['fc_interesting'])+1)\r
34             trim = current.curve.trimindexes()[lower:upper]\r
35             newtime = []\r
36             newzpiezo = []\r
37             newphase = []\r
38             for x in range(trim[0],trim[1]):\r
39                 newtime.append(self.plots[0].vectors[0][0][x])\r
40                 newzpiezo.append(self.plots[0].vectors[0][1][x])\r
41                 newphase.append(self.plots[0].vectors[1][1][x])\r
42             self.plots[0].vectors[0][0] = newtime\r
43             self.plots[0].vectors[0][1] = newzpiezo\r
44             self.plots[0].vectors[1][0] = newtime\r
45             self.plots[0].vectors[1][1] = newphase\r
46 \r
47         if self.config['fc_interesting'] != 0 and plot.destination==1:\r
48             lower = int((self.config['fc_interesting'])-1)\r
49             upper = int((self.config['fc_interesting'])+1)\r
50             trim = current.curve.trimindexes()[lower:upper]\r
51             newtime = []\r
52             newdefl = []\r
53             newimposed = []\r
54             for x in range(trim[0],trim[1]):\r
55                 newtime.append(self.plots[1].vectors[0][0][x])\r
56                 newdefl.append(self.plots[1].vectors[0][1][x])\r
57                 newimposed.append(self.plots[1].vectors[1][1][x])\r
58             self.plots[1].vectors[0][0] = newtime\r
59             self.plots[1].vectors[0][1] = newdefl\r
60             self.plots[1].vectors[1][0] = newtime\r
61             self.plots[1].vectors[1][1] = newimposed            \r
62                         \r
63         if self.config['fc_showphase'] == 0 and plot.destination==0:\r
64             self.plots[0].remove_set(1)\r
65             \r
66         if self.config['fc_showimposed'] == 0 and plot.destination==1:\r
67             self.plots[1].remove_set(1)\r
68                          \r
69         return plot
70       
71     def do_time(self,args):
72         '''
73         Measures the time difference (in seconds) between two points
74         Implemented only for force clamp
75         ----
76         Syntax: time
77         '''
78         if self.current.curve.experiment == 'clamp':\r
79             time=self._delta(set=0)[0]\r
80             print str(time*1000)+' ms'
81         else:
82             print 'This command makes no sense for a non-force clamp experiment.'
83             
84     def do_zpiezo(self,args):
85         '''
86         Measures the zpiezo difference (in nm) between two points
87         Implemented only for force clamp
88         ----
89         Syntax: zpiezo
90         '''
91         if self.current.curve.experiment == 'clamp':
92             zpiezo=self._delta(set=0)[2]\r
93             print str(zpiezo*(10**9))+' nm'
94         else:
95             print 'This command makes no sense for a non-force clamp experiment.'
96             
97     def do_defl(self,args):
98         '''
99         Measures the deflection difference (in nm) between two points
100         Implemented only for force clamp
101         NOTE: It makes sense only on the time VS defl plot; it is still not masked for the other plot...
102         -----
103         Syntax: defl
104         '''
105         if self.current.curve.experiment == 'clamp':
106             print "Warning - don't use on the zpiezo plot!"
107             defl=self._delta(set=1)[2]
108             print str(defl*(10**12))+' pN'
109         else:
110             print 'This command makes no sense for a non-force clamp experiment.'
111             
112     def do_step(self,args):
113         '''
114         Measures the length and time duration of a time-Z step
115         -----
116         Syntax: step
117         '''
118         if self.current.curve.experiment == 'clamp':
119             print 'Click three points in this fashion:'
120             print ' (0)-------(1)'
121             print '           |'
122             print '           |'
123             print '           (2)----------'
124             points=self._measure_N_points(N=3,whatset=0)
125             dz=abs(points[2].graph_coords[1]-points[1].graph_coords[1])*(10e+8)
126             dt=abs(points[1].graph_coords[0]-points[0].graph_coords[0])
127             print 'dZ: ',dz,' nm'
128             print 'dT: ',dt,' s'
129             
130         else:
131             print 'This command makes no sense for a non-force clamp experiment.'\r
132 \r
133     def do_fcfilt(self,args):\r
134         '''\r
135         Filters out featureless force clamp curves of the current playlist.\r
136         It's very similar to 'flatfilt' for velocity clamp curves.\r
137         Creates a new playlist only containing non-empty curves.\r
138 \r
139         WARNING - Only works if you set an appropriate fc_interesting config variable!\r
140         WARNING - arguments are NOT optional at the moment!\r
141 \r
142         Syntax: fcfilt maxretraction(nm) mindeviation (pN)\r
143 \r
144         Suggested values for an (i27)8 experiment with our setup are 200nm and 10-15 pN\r
145         '''\r
146 \r
147         if self.config['fc_interesting'] == 0:\r
148             print 'You must specify the phase of interest (using set fc_interesing X) prior to running fcfilt!'\r
149             return\r
150         \r
151         maxretraction=0\r
152         threshold=0\r
153         args=args.split(' ')\r
154         if len(args)==2:\r
155             maxretraction=int(args[0])\r
156             threshold=int(args[1])\r
157         else:\r
158             print 'Arguments are not optional for fcfilt. You should pass two numbers:'\r
159             print '(1) the maximum plausible piezo retraction in NANOMETERS (e.g. the length of the protein)'\r
160             print "(2) the threshold, in PICONEWTONS. If signal deviates from imposed more than this, it's an event"\r
161             return\r
162         \r
163 \r
164         print 'Processing playlist... go get yourself a cup of coffee.'\r
165         notflat_list=[]\r
166 \r
167         c=0\r
168 \r
169         for item in self.current_list:\r
170             c+=1\r
171             try:\r
172                 notflat=self.has_stuff(item,maxretraction,threshold)\r
173                 print 'Curve',item.path,'is',c,'of',len(self.current_list),'--->Has Stuff =',notflat\r
174             except:\r
175                 notflat=False\r
176                 print 'Curve',item.path,'is',c,'of',len(self.current_list),'--->could not be processed'\r
177             if notflat:\r
178                 item.features=notflat\r
179                 item.curve=None\r
180                 notflat_list.append(item)\r
181 \r
182         if len(notflat_list)==0:\r
183             print 'Nothing interesting here. Reconsider either your filtering criteria or your experimental data'\r
184             return\r
185         else:\r
186             print 'Found',len(notflat_list),'potentially interesting curves.'\r
187             print 'Regenerating Playlist...'\r
188             self.pointer=0\r
189             self.current_list=notflat_list\r
190             self.current=self.current_list[self.pointer]\r
191             self.do_plot(0)\r
192 \r
193     def has_stuff(self,item,maxretraction,threshold):\r
194         '''\r
195         Decides whether a curve has some features in the interesting phase.\r
196         Algorithm:\r
197             - clip the interesting phase portion of the curve.\r
198             - discard the first 20 milliseconds (this is due to a quirk of our hardware).\r
199             - look at the zpiezo plot and note down when (if) retratcs more than [maxretraction] nm away from the first point.\r
200             - clip off any data after this point, with an excess of 100 points (again, an hardware quirk)\r
201             - if the remainder is less than 100 points, ditch the curve.\r
202             - now look at the deflection plot and check if there are points more than [threshold] pN over the 'flat zone'.\r
203             - if you find such points, bingo!            \r
204         '''\r
205 \r
206         item.identify(self.drivers)\r
207    \r
208         lower = int((self.config['fc_interesting'])-1)\r
209         upper = int((self.config['fc_interesting'])+1)\r
210         trim_idxs = item.curve.trimindexes()[lower:upper]\r
211         lo=trim_idxs[0]+20                                                  #clipping the first 20 points off...\r
212         hi=trim_idxs[1]\r
213         trimmed_zpiezo=item.curve.default_plots()[0].vectors[0][1][lo:hi]\r
214         trimmed_defl=item.curve.default_plots()[1].vectors[0][1][lo:hi]\r
215         trimmed_imposed=item.curve.default_plots()[1].vectors[1][1][lo:hi]\r
216         imposed=trimmed_imposed[21]                                         #just to match the 20-pts clipping...\r
217         \r
218         item.curve.close_all()\r
219         del item.curve\r
220         del item\r
221 \r
222         starting_z=trimmed_zpiezo[0]\r
223         plausible=starting_z-(maxretraction*1e-9)\r
224         det_trim=0\r
225         while trimmed_zpiezo[det_trim]>plausible:\r
226             det_trim+=1\r
227             if det_trim >= len(trimmed_zpiezo):                              #breaking cycles makes me shiver...\r
228                 det_trim=len(trimmed_zpiezo)                                 #but I cannot think of anything better now.\r
229                 break\r
230         further_trim=det_trim-100\r
231         if further_trim<100:\r
232             return False\r
233         trimmed_defl=trimmed_defl[:further_trim]\r
234 \r
235         trimmed_defl.sort()\r
236         ninetypercent=int(0.9*len(trimmed_defl))\r
237         j=0\r
238         sum=0\r
239         for j in trimmed_defl[:ninetypercent]:\r
240             sum+=j\r
241         avg=float(sum/ninetypercent)\r
242         sweetspot=float(avg+(threshold*1e-12))\r
243         if trimmed_defl[-1]>sweetspot:\r
244             flag=True\r
245         else:\r
246             flag=False\r
247 \r
248         del trimmed_defl,trimmed_zpiezo,trimmed_imposed            \r
249 \r
250         return flag            \r
251