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