6 Plugin regarding general velocity clamp measurements
9 from libhooke import WX_GOOD, ClickedPoint
11 wxversion.select(WX_GOOD)
12 from wx import PostEvent
20 warnings.simplefilter('ignore',np.RankWarning)
23 class generalvclampCommands:
30 def do_distance(self,args):
34 Measure the distance (in nm) between two points.
35 For a standard experiment this is the delta X distance.
36 For a force clamp experiment this is the delta Y distance (actually becomes
41 if self.current.curve.experiment == 'clamp':
42 print 'You wanted to use zpiezo perhaps?'
45 dx,unitx,dy,unity=self._delta(set=1)
46 print str(dx*(10**9))+' nm'
47 to_dump='distance '+self.current.path+' '+str(dx*(10**9))+' nm'
48 self.outlet.push(to_dump)
51 def do_force(self,args):
55 Measure the force difference (in pN) between two points
59 if self.current.curve.experiment == 'clamp':
60 print 'This command makes no sense for a force clamp experiment.'
62 dx,unitx,dy,unity=self._delta(set=1)
63 print str(dy*(10**12))+' pN'
64 to_dump='force '+self.current.path+' '+str(dy*(10**12))+' pN'
65 self.outlet.push(to_dump)
68 def do_forcebase(self,args):
72 Measures the difference in force (in pN) between a point and a baseline
73 took as the average between two points.
75 The baseline is fixed once for a given curve and different force measurements,
76 unless the user wants it to be recalculated
78 Syntax: forcebase [rebase]
79 rebase: Forces forcebase to ask again the baseline
80 max: Instead of asking for a point to measure, asks for two points and use
81 the maximum peak in between
83 rebase=False #if true=we select rebase
84 maxpoint=False #if true=we measure the maximum peak
86 plot=self._get_displayed_plot()
87 whatset=1 #fixme: for all sets
88 if 'rebase' in args or (self.basecurrent != self.current.path):
94 print 'Select baseline'
95 self.basepoints=self._measure_N_points(N=2, whatset=whatset)
96 self.basecurrent=self.current.path
99 print 'Select two points'
100 points=self._measure_N_points(N=2, whatset=whatset)
101 boundpoints=[points[0].index, points[1].index]
104 y=min(plot.vectors[whatset][1][boundpoints[0]:boundpoints[1]])
106 print 'Chosen interval not valid. Try picking it again. Did you pick the same point as begin and end of interval?'
108 print 'Select point to measure'
109 points=self._measure_N_points(N=1, whatset=whatset)
110 #whatplot=points[0].dest
111 y=points[0].graph_coords[1]
113 #fixme: code duplication
114 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
116 to_average=plot.vectors[whatset][1][boundaries[0]:boundaries[1]] #y points to average
118 avg=np.mean(to_average)
120 print str(forcebase*(10**12))+' pN'
121 to_dump='forcebase '+self.current.path+' '+str(forcebase*(10**12))+' pN'
122 self.outlet.push(to_dump)
125 def plotmanip_flatten(self, plot, current, customvalue=False):
127 Subtracts a polynomial fit to the non-contact part of the curve, as to flatten it.
128 the best polynomial fit is chosen among polynomials of degree 1 to n, where n is
129 given by the configuration file or by the customvalue.
131 customvalue= int (>0) --> starts the function even if config says no (default=False)
135 if current.curve.experiment != 'smfs':
138 #only one set is present...
139 if len(self.plots[0].vectors) != 2:
142 #config is not flatten, and customvalue flag is false too
143 if (not self.config['flatten']) and (not customvalue):
150 max_cycles=customvalue
152 max_cycles=self.config['flatten'] #Using > 1 usually doesn't help and can give artefacts. However, it could be useful too.
154 contact_index=self.find_contact_point()
156 valn=[[] for item in range(max_exponent)]
157 yrn=[0.0 for item in range(max_exponent)]
158 errn=[0.0 for item in range(max_exponent)]
160 for i in range(int(max_cycles)):
162 x_ext=plot.vectors[0][0][contact_index+delta_contact:]
163 y_ext=plot.vectors[0][1][contact_index+delta_contact:]
164 x_ret=plot.vectors[1][0][contact_index+delta_contact:]
165 y_ret=plot.vectors[1][1][contact_index+delta_contact:]
166 for exponent in range(max_exponent):
168 valn[exponent]=sp.polyfit(x_ext,y_ext,exponent)
169 yrn[exponent]=sp.polyval(valn[exponent],x_ret)
170 errn[exponent]=sp.sqrt(sum((yrn[exponent]-y_ext)**2)/float(len(y_ext)))
172 print 'Cannot flatten!'
175 best_exponent=errn.index(min(errn))
178 ycorr_ext=y_ext-yrn[best_exponent]+y_ext[0] #noncontact part
179 yjoin_ext=np.array(plot.vectors[0][1][0:contact_index+delta_contact]) #contact part
181 ycorr_ret=y_ret-yrn[best_exponent]+y_ext[0] #noncontact part
182 yjoin_ret=np.array(plot.vectors[1][1][0:contact_index+delta_contact]) #contact part
184 ycorr_ext=np.concatenate((yjoin_ext, ycorr_ext))
185 ycorr_ret=np.concatenate((yjoin_ret, ycorr_ret))
187 plot.vectors[0][1]=list(ycorr_ext)
188 plot.vectors[1][1]=list(ycorr_ret)
193 def do_slope(self,args):
197 Measures the slope of a delimited chunk on the return trace.
198 The chunk can be delimited either by two manual clicks, or have
199 a fixed width, given as an argument.
201 Syntax: slope [width]
202 The facultative [width] parameter specifies how many
203 points will be considered for the fit. If [width] is
204 specified, only one click will be required.
205 (c) Marco Brucale, Massimo Sandal 2008
208 # Reads the facultative width argument
214 # Decides between the two forms of user input, as per (args)
216 # Gets the Xs of two clicked points as indexes on the current curve vector
217 print 'Click twice to delimit chunk'
219 points=self._measure_N_points(N=2,whatset=1)
220 clickedpoints=[points[0].index,points[1].index]
223 print 'Click once on the leftmost point of the chunk (i.e.usually the peak)'
225 points=self._measure_N_points(N=1,whatset=1)
226 clickedpoints=[points[0].index-fitspan,points[0].index]
228 # Calls the function linefit_between
229 parameters=[0,0,[],[]]
230 parameters=self.linefit_between(clickedpoints[0],clickedpoints[1])
232 # Outputs the relevant slope parameter
234 print str(parameters[0])
235 to_dump='slope '+self.current.path+' '+str(parameters[0])
236 self.outlet.push(to_dump)
238 # Makes a vector with the fitted parameters and sends it to the GUI
239 xtoplot=parameters[2]
243 ytoplot.append((x*parameters[0])+parameters[1])
245 clickvector_x, clickvector_y=[], []
247 clickvector_x.append(item.graph_coords[0])
248 clickvector_y.append(item.graph_coords[1])
250 lineplot=self._get_displayed_plot(0) #get topmost displayed plot
252 lineplot.add_set(xtoplot,ytoplot)
253 lineplot.add_set(clickvector_x, clickvector_y)
255 if lineplot.styles==[]:
256 lineplot.styles=[None,None,None,'scatter']
258 lineplot.styles+=[None,'scatter']
260 self._send_plot([lineplot])
262 def linefit_between(self,index1,index2):
264 Creates two vectors (xtofit,ytofit) slicing out from the
265 current return trace a portion delimited by the two indexes
267 Then does a least squares linear fit on that slice.
268 Finally returns [0]=the slope, [1]=the intercept of the
269 fitted 1st grade polynomial, and [2,3]=the actual (x,y) vectors
271 (c) Marco Brucale, Massimo Sandal 2008
273 # Translates the indexes into two vectors containing the x,y data to fit
274 xtofit=self.plots[0].vectors[1][0][index1:index2]
275 ytofit=self.plots[0].vectors[1][1][index1:index2]
277 # Does the actual linear fitting (simple least squares with numpy.polyfit)
279 linefit=np.polyfit(xtofit,ytofit,1)
281 return (linefit[0],linefit[1],xtofit,ytofit)
283 #====================
285 #====================
286 def do_autopeak(self,args):
290 Automatically performs a number of analyses on the peaks of the given curve.
291 Currently it automatically:
292 - fits peaks with WLC function
293 - measures peak maximum forces with a baseline
294 - measures slope in proximity of peak maximum
295 Requires flatten plotmanipulator , fit.py plugin , flatfilts.py plugin with convfilt
298 autopeak [rebase] [pl=value] [t=value] [noauto] [reclick]
300 rebase : Re-asks baseline interval
302 pl=[value] : Use a fixed persistent length for the fit. If pl is not given,
303 the fit will be a 2-variable
304 fit. DO NOT put spaces between 'pl', '=' and the value.
305 The value must be in meters.
306 Scientific notation like 0.35e-9 is fine.
308 t=[value] : Use a user-defined temperature. The value must be in
309 kelvins; by default it is 293 K.
310 DO NOT put spaces between 't', '=' and the value.
312 noauto : allows for clicking the contact point by
313 hand (otherwise it is automatically estimated) the first time.
314 If subsequent measurements are made, the same contact point
315 clicked the first time is used
317 reclick : redefines by hand the contact point, if noauto has been used before
318 but the user is unsatisfied of the previously choosen contact point.
320 When you first issue the command, it will ask for the filename. If you are giving the filename
321 of an existing file, autopeak will resume it and append measurements to it. If you are giving
322 a new filename, it will create the file and append to it until you close Hooke.
324 Useful variables (to set with SET command):
326 temperature= temperature of the system for wlc fit (in K)
327 auto_fit_points = number of points to fit before the peak maximum, for wlc
328 auto_slope_span = number of points on which measure the slope, for slope
332 T=self.config['temperature']
334 fit_points=int(self.config['auto_fit_points'])
335 slope_span=int(self.config['auto_slope_span'])
338 rebase=False #if true=we select rebase
341 displayed_plot=self._get_displayed_plot(0)
343 if self.current.curve.experiment != 'smfs' or len(displayed_plot.vectors) < 2:
344 print 'Cannot work on this curve.'
347 #Look for custom persistent length / custom temperature
348 for arg in args.split():
349 #look for a persistent length argument.
351 pl_expression=arg.split('=')
352 pl_value=float(pl_expression[1]) #actual value
353 #look for a T argument. FIXME: spaces are not allowed between 'pl' and value
354 if ('t=' in arg[0:2]) or ('T=' in arg[0:2]):
355 t_expression=arg.split('=')
356 T=float(t_expression[1])
358 #Handle contact point arguments
359 #FIXME: code duplication
360 if 'reclick' in args.split():
361 print 'Click contact point'
362 contact_point=self._measure_N_points(N=1, whatset=1)[0]
363 contact_point_index=contact_point.index
364 self.wlccontact_point=contact_point
365 self.wlccontact_index=contact_point.index
366 self.wlccurrent=self.current.path
367 elif 'noauto' in args.split():
368 if self.wlccontact_index==None or self.wlccurrent != self.current.path:
369 print 'Click contact point'
370 contact_point=self._measure_N_points(N=1, whatset=1)[0]
371 contact_point_index=contact_point.index
372 self.wlccontact_point=contact_point
373 self.wlccontact_index=contact_point.index
374 self.wlccurrent=self.current.path
376 contact_point=self.wlccontact_point
377 contact_point_index=self.wlccontact_index
379 #Automatically find contact point
380 cindex=self.find_contact_point()
381 contact_point=ClickedPoint()
382 contact_point.absolute_coords=displayed_plot.vectors[1][0][cindex], displayed_plot.vectors[1][1][cindex]
383 contact_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
384 contact_point.is_marker=True
387 #Pick up force baseline
388 whatset=1 #fixme: for all sets
389 if 'rebase' in args or (self.basecurrent != self.current.path):
392 print 'Select baseline'
393 self.basepoints=self._measure_N_points(N=2, whatset=whatset)
394 self.basecurrent=self.current.path
395 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
397 to_average=displayed_plot.vectors[1][1][boundaries[0]:boundaries[1]] #y points to average
398 avg=np.mean(to_average)
401 defplot=self.current.curve.default_plots()[0]
402 flatten=self._find_plotmanip('flatten') #Extract flatten plotmanip
403 defplot=flatten(defplot, self.current, customvalue=1) #Flatten curve before feeding it to has_peaks
404 peak_location,peak_size=self.has_peaks(defplot, self.convfilt_config['mindeviation'])
406 #Create a new plot to send
407 fitplot=copy.deepcopy(displayed_plot)
409 #Initialize data vectors
415 #Cycle between peaks and do analysis.
416 for peak in peak_location:
418 #FIXME: clean wlc fitting, to avoid this clickedpoint hell
419 #-create a clicked point for the peak point
420 peak_point=ClickedPoint()
421 peak_point.absolute_coords=displayed_plot.vectors[1][0][peak], displayed_plot.vectors[1][1][peak]
422 peak_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
423 #-create a clicked point for the other fit point
424 other_fit_point=ClickedPoint()
425 other_fit_point.absolute_coords=displayed_plot.vectors[1][0][peak-fit_points], displayed_plot.vectors[1][1][peak-fit_points]
426 other_fit_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
428 points=[contact_point, peak_point, other_fit_point]
429 params, yfit, xfit = self.wlc_fit(points, displayed_plot.vectors[1][0], displayed_plot.vectors[1][1],pl_value,T)
430 #save wlc values (nm)
431 c_lengths.append(params[0]*(1.0e+9))
432 if len(params)==2: #if we did choose 2-value fit
433 p_lengths.append(params[1]*(1.0e+9))
435 p_lengths.append(pl_value)
436 #Add WLC fit lines to plot
437 fitplot.add_set(xfit,yfit)
438 if len(fitplot.styles)==0:
441 fitplot.styles.append(None)
444 delta_to_measure=displayed_plot.vectors[1][1][peak-delta_force:peak+delta_force]
445 y=min(delta_to_measure)
446 #save force values (pN)
447 forces.append(abs(y-avg)*(1.0e+12))
450 slope=self.linefit_between(peak-slope_span,peak)[0]
453 #Show wlc fits and peak locations
454 self._send_plot([fitplot])
457 #Ask the user what peaks to ignore from analysis.
458 print 'Peaks to ignore (0,1...n from contact point,return to take all)'
459 print 'N to discard measurement'
460 exclude_raw=raw_input('Input:')
464 if not exclude_raw=='':
465 exclude=exclude_raw.split(',')
467 exclude=[int(item) for item in exclude]
474 print 'Bad input, taking all...'
475 #Clean data vectors from ignored peaks
476 c_lengths=[item for item in c_lengths if item != None]
477 p_lengths=[item for item in p_lengths if item != None]
478 forces=[item for item in forces if item != None]
479 slopes=[item for item in slopes if item != None]
480 print 'contour (nm)',c_lengths
481 print 'p (nm)',p_lengths
482 print 'forces (pN)',forces
483 print 'slopes (N/m)',slopes
486 if self.autofile=='':
487 self.autofile=raw_input('Filename? (return to ignore) ')
488 if self.autofile=='':
492 if not os.path.exists(self.autofile):
493 f=open(self.autofile,'w+')
494 f.write('Analysis started '+time.asctime()+'\n')
495 f.write('----------------------------------------\n')
496 f.write('; Contour length (nm) ; Persistence length (nm) ; Max.Force (pN) ; Slope (N/m) \n')
500 f=open(self.autofile,'a+')
502 f.write(self.current.path+'\n')
503 for i in range(len(c_lengths)):
504 f.write(' ; '+str(c_lengths[i])+' ; '+str(p_lengths[i])+' ; '+str(forces[i])+' ; '+str(slopes[i])+'\n')
507 self.do_note('autopeak')