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