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'
49 def do_force(self,args):
53 Measure the force difference (in pN) between two points
57 if self.current.curve.experiment == 'clamp':
58 print 'This command makes no sense for a force clamp experiment.'
60 dx,unitx,dy,unity=self._delta(set=1)
61 print str(dy*(10**12))+' pN'
64 def do_forcebase(self,args):
68 Measures the difference in force (in pN) between a point and a baseline
69 took as the average between two points.
71 The baseline is fixed once for a given curve and different force measurements,
72 unless the user wants it to be recalculated
74 Syntax: forcebase [rebase]
75 rebase: Forces forcebase to ask again the baseline
76 max: Instead of asking for a point to measure, asks for two points and use
77 the maximum peak in between
79 rebase=False #if true=we select rebase
80 maxpoint=False #if true=we measure the maximum peak
82 plot=self._get_displayed_plot()
83 whatset=1 #fixme: for all sets
84 if 'rebase' in args or (self.basecurrent != self.current.path):
90 print 'Select baseline'
91 self.basepoints=self._measure_N_points(N=2, whatset=whatset)
92 self.basecurrent=self.current.path
95 print 'Select two points'
96 points=self._measure_N_points(N=2, whatset=whatset)
97 boundpoints=[points[0].index, points[1].index]
100 y=min(plot.vectors[whatset][1][boundpoints[0]:boundpoints[1]])
102 print 'Chosen interval not valid. Try picking it again. Did you pick the same point as begin and end of interval?'
104 print 'Select point to measure'
105 points=self._measure_N_points(N=1, whatset=whatset)
106 #whatplot=points[0].dest
107 y=points[0].graph_coords[1]
109 #fixme: code duplication
110 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
112 to_average=plot.vectors[whatset][1][boundaries[0]:boundaries[1]] #y points to average
114 avg=np.mean(to_average)
116 print str(forcebase*(10**12))+' pN'
119 def plotmanip_flatten(self, plot, current, customvalue=False):
121 Subtracts a polynomial fit to the non-contact part of the curve, as to flatten it.
122 the best polynomial fit is chosen among polynomials of degree 1 to n, where n is
123 given by the configuration file or by the customvalue.
125 customvalue= int (>0) --> starts the function even if config says no (default=False)
129 if current.curve.experiment != 'smfs':
132 #only one set is present...
133 if len(self.plots[0].vectors) != 2:
136 #config is not flatten, and customvalue flag is false too
137 if (not self.config['flatten']) and (not customvalue):
144 max_cycles=customvalue
146 max_cycles=self.config['flatten'] #Using > 1 usually doesn't help and can give artefacts. However, it could be useful too.
148 contact_index=self.find_contact_point()
150 valn=[[] for item in range(max_exponent)]
151 yrn=[0.0 for item in range(max_exponent)]
152 errn=[0.0 for item in range(max_exponent)]
154 for i in range(int(max_cycles)):
156 x_ext=plot.vectors[0][0][contact_index+delta_contact:]
157 y_ext=plot.vectors[0][1][contact_index+delta_contact:]
158 x_ret=plot.vectors[1][0][contact_index+delta_contact:]
159 y_ret=plot.vectors[1][1][contact_index+delta_contact:]
160 for exponent in range(max_exponent):
162 valn[exponent]=sp.polyfit(x_ext,y_ext,exponent)
163 yrn[exponent]=sp.polyval(valn[exponent],x_ret)
164 errn[exponent]=sp.sqrt(sum((yrn[exponent]-y_ext)**2)/float(len(y_ext)))
166 print 'Cannot flatten!'
169 best_exponent=errn.index(min(errn))
172 ycorr_ext=y_ext-yrn[best_exponent]+y_ext[0] #noncontact part
173 yjoin_ext=np.array(plot.vectors[0][1][0:contact_index+delta_contact]) #contact part
175 ycorr_ret=y_ret-yrn[best_exponent]+y_ext[0] #noncontact part
176 yjoin_ret=np.array(plot.vectors[1][1][0:contact_index+delta_contact]) #contact part
178 ycorr_ext=np.concatenate((yjoin_ext, ycorr_ext))
179 ycorr_ret=np.concatenate((yjoin_ret, ycorr_ret))
181 plot.vectors[0][1]=list(ycorr_ext)
182 plot.vectors[1][1]=list(ycorr_ret)
187 def do_slope(self,args):
191 Measures the slope of a delimited chunk on the return trace.
192 The chunk can be delimited either by two manual clicks, or have
193 a fixed width, given as an argument.
195 Syntax: slope [width]
196 The facultative [width] parameter specifies how many
197 points will be considered for the fit. If [width] is
198 specified, only one click will be required.
199 (c) Marco Brucale, Massimo Sandal 2008
202 # Reads the facultative width argument
208 # Decides between the two forms of user input, as per (args)
210 # Gets the Xs of two clicked points as indexes on the current curve vector
211 print 'Click twice to delimit chunk'
213 points=self._measure_N_points(N=2,whatset=1)
214 clickedpoints=[points[0].index,points[1].index]
217 print 'Click once on the leftmost point of the chunk (i.e.usually the peak)'
219 points=self._measure_N_points(N=1,whatset=1)
220 clickedpoints=[points[0].index-fitspan,points[0].index]
222 # Calls the function linefit_between
223 parameters=[0,0,[],[]]
224 parameters=self.linefit_between(clickedpoints[0],clickedpoints[1])
226 # Outputs the relevant slope parameter
228 print str(parameters[0])
230 # Makes a vector with the fitted parameters and sends it to the GUI
231 xtoplot=parameters[2]
235 ytoplot.append((x*parameters[0])+parameters[1])
237 clickvector_x, clickvector_y=[], []
239 clickvector_x.append(item.graph_coords[0])
240 clickvector_y.append(item.graph_coords[1])
242 lineplot=self._get_displayed_plot(0) #get topmost displayed plot
244 lineplot.add_set(xtoplot,ytoplot)
245 lineplot.add_set(clickvector_x, clickvector_y)
247 if lineplot.styles==[]:
248 lineplot.styles=[None,None,None,'scatter']
250 lineplot.styles+=[None,'scatter']
252 self._send_plot([lineplot])
254 def linefit_between(self,index1,index2):
256 Creates two vectors (xtofit,ytofit) slicing out from the
257 current return trace a portion delimited by the two indexes
259 Then does a least squares linear fit on that slice.
260 Finally returns [0]=the slope, [1]=the intercept of the
261 fitted 1st grade polynomial, and [2,3]=the actual (x,y) vectors
263 (c) Marco Brucale, Massimo Sandal 2008
265 # Translates the indexes into two vectors containing the x,y data to fit
266 xtofit=self.plots[0].vectors[1][0][index1:index2]
267 ytofit=self.plots[0].vectors[1][1][index1:index2]
269 # Does the actual linear fitting (simple least squares with numpy.polyfit)
271 linefit=np.polyfit(xtofit,ytofit,1)
273 return (linefit[0],linefit[1],xtofit,ytofit)
275 #====================
277 #====================
278 def do_autopeak(self,args):
282 Automatically performs a number of analyses on the peaks of the given curve.
283 Currently it automatically:
284 - fits peaks with WLC function
285 - measures peak maximum forces with a baseline
286 - measures slope in proximity of peak maximum
287 Requires flatten plotmanipulator , fit.py plugin , flatfilts.py plugin with convfilt
290 autopeak [rebase] [pl=value] [t=value] [noauto] [reclick]
292 rebase : Re-asks baseline interval
294 pl=[value] : Use a fixed persistent length for the fit. If pl is not given,
295 the fit will be a 2-variable
296 fit. DO NOT put spaces between 'pl', '=' and the value.
297 The value must be in meters.
298 Scientific notation like 0.35e-9 is fine.
300 t=[value] : Use a user-defined temperature. The value must be in
301 kelvins; by default it is 293 K.
302 DO NOT put spaces between 't', '=' and the value.
304 noauto : allows for clicking the contact point by
305 hand (otherwise it is automatically estimated) the first time.
306 If subsequent measurements are made, the same contact point
307 clicked the first time is used
309 reclick : redefines by hand the contact point, if noauto has been used before
310 but the user is unsatisfied of the previously choosen contact point.
312 When you first issue the command, it will ask for the filename. If you are giving the filename
313 of an existing file, autopeak will resume it and append measurements to it. If you are giving
314 a new filename, it will create the file and append to it until you close Hooke.
316 Useful variables (to set with SET command):
318 temperature= temperature of the system for wlc fit (in K)
319 auto_fit_points = number of points to fit before the peak maximum, for wlc
320 auto_slope_span = number of points on which measure the slope, for slope
324 T=self.config['temperature']
326 fit_points=int(self.config['auto_fit_points'])
327 slope_span=int(self.config['auto_slope_span'])
330 rebase=False #if true=we select rebase
333 displayed_plot=self._get_displayed_plot(0)
335 if self.current.curve.experiment != 'smfs' or len(displayed_plot.vectors) < 2:
336 print 'Cannot work on this curve.'
339 #Look for custom persistent length / custom temperature
340 for arg in args.split():
341 #look for a persistent length argument.
343 pl_expression=arg.split('=')
344 pl_value=float(pl_expression[1]) #actual value
345 #look for a T argument. FIXME: spaces are not allowed between 'pl' and value
346 if ('t=' in arg[0:2]) or ('T=' in arg[0:2]):
347 t_expression=arg.split('=')
348 T=float(t_expression[1])
350 #Handle contact point arguments
351 #FIXME: code duplication
352 if 'reclick' in args.split():
353 print 'Click contact point'
354 contact_point=self._measure_N_points(N=1, whatset=1)[0]
355 contact_point_index=contact_point.index
356 self.wlccontact_point=contact_point
357 self.wlccontact_index=contact_point.index
358 self.wlccurrent=self.current.path
359 elif 'noauto' in args.split():
360 if self.wlccontact_index==None or self.wlccurrent != self.current.path:
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
368 contact_point=self.wlccontact_point
369 contact_point_index=self.wlccontact_index
371 #Automatically find contact point
372 cindex=self.find_contact_point()
373 contact_point=ClickedPoint()
374 contact_point.absolute_coords=displayed_plot.vectors[1][0][cindex], displayed_plot.vectors[1][1][cindex]
375 contact_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
376 contact_point.is_marker=True
379 #Pick up force baseline
380 whatset=1 #fixme: for all sets
381 if 'rebase' in args or (self.basecurrent != self.current.path):
384 print 'Select baseline'
385 self.basepoints=self._measure_N_points(N=2, whatset=whatset)
386 self.basecurrent=self.current.path
387 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
389 to_average=displayed_plot.vectors[1][1][boundaries[0]:boundaries[1]] #y points to average
390 avg=np.mean(to_average)
393 defplot=self.current.curve.default_plots()[0]
394 flatten=self._find_plotmanip('flatten') #Extract flatten plotmanip
395 defplot=flatten(defplot, self.current, customvalue=1) #Flatten curve before feeding it to has_peaks
396 peak_location,peak_size=self.has_peaks(defplot, self.convfilt_config['mindeviation'])
398 #Create a new plot to send
399 fitplot=copy.deepcopy(displayed_plot)
401 #Initialize data vectors
407 #Cycle between peaks and do analysis.
408 for peak in peak_location:
410 #FIXME: clean wlc fitting, to avoid this clickedpoint hell
411 #-create a clicked point for the peak point
412 peak_point=ClickedPoint()
413 peak_point.absolute_coords=displayed_plot.vectors[1][0][peak], displayed_plot.vectors[1][1][peak]
414 peak_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
415 #-create a clicked point for the other fit point
416 other_fit_point=ClickedPoint()
417 other_fit_point.absolute_coords=displayed_plot.vectors[1][0][peak-fit_points], displayed_plot.vectors[1][1][peak-fit_points]
418 other_fit_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
420 points=[contact_point, peak_point, other_fit_point]
421 params, yfit, xfit = self.wlc_fit(points, displayed_plot.vectors[1][0], displayed_plot.vectors[1][1],pl_value,T)
422 #save wlc values (nm)
423 c_lengths.append(params[0]*(1.0e+9))
424 if len(params)==2: #if we did choose 2-value fit
425 p_lengths.append(params[1]*(1.0e+9))
427 p_lengths.append(pl_value)
428 #Add WLC fit lines to plot
429 fitplot.add_set(xfit,yfit)
430 if len(fitplot.styles)==0:
433 fitplot.styles.append(None)
436 delta_to_measure=displayed_plot.vectors[1][1][peak-delta_force:peak+delta_force]
437 y=min(delta_to_measure)
438 #save force values (pN)
439 forces.append(abs(y-avg)*(1.0e+12))
442 slope=self.linefit_between(peak-slope_span,peak)[0]
445 #Show wlc fits and peak locations
446 self._send_plot([fitplot])
449 #Ask the user what peaks to ignore from analysis.
450 print 'Peaks to ignore (0,1...n from contact point,return to take all)'
451 print 'N to discard measurement'
452 exclude_raw=raw_input('Input:')
456 if not exclude_raw=='':
457 exclude=exclude_raw.split(',')
459 exclude=[int(item) for item in exclude]
466 print 'Bad input, taking all...'
467 #Clean data vectors from ignored peaks
468 c_lengths=[item for item in c_lengths if item != None]
469 p_lengths=[item for item in p_lengths if item != None]
470 forces=[item for item in forces if item != None]
471 slopes=[item for item in slopes if item != None]
472 print 'contour (nm)',c_lengths
473 print 'p (nm)',p_lengths
474 print 'forces (pN)',forces
475 print 'slopes (N/m)',slopes
478 if self.autofile=='':
479 self.autofile=raw_input('Filename? (return to ignore) ')
480 if self.autofile=='':
484 if not os.path.exists(self.autofile):
485 f=open(self.autofile,'w+')
486 f.write('Analysis started '+time.asctime()+'\n')
487 f.write('----------------------------------------\n')
488 f.write('; Contour length (nm) ; Persistence length (nm) ; Max.Force (pN) ; Slope (N/m) \n')
492 f=open(self.autofile,'a+')
494 f.write(self.current.path+'\n')
495 for i in range(len(c_lengths)):
496 f.write(' ; '+str(c_lengths[i])+' ; '+str(p_lengths[i])+' ; '+str(forces[i])+' ; '+str(slopes[i])+'\n')
499 self.do_note('autopeak')