2 # -*- coding: iso-8859-1 -*-
7 Plugin regarding general velocity clamp measurements
10 from hooke.libhooke import WX_GOOD, ClickedPoint
12 wxversion.select(WX_GOOD)
13 from wx import PostEvent
21 warnings.simplefilter('ignore',np.RankWarning)
24 class generalvclampCommands(object):
31 def do_distance(self,args):
35 Measure the distance (in nm) between two points.
36 For a standard experiment this is the delta X distance.
37 For a force clamp experiment this is the delta Y distance (actually becomes
42 if self.current.curve.experiment == 'clamp':
43 print 'You wanted to use zpiezo perhaps?'
46 dx,unitx,dy,unity=self._delta(set=1)
47 print str(dx*(10**9))+' nm'
48 to_dump='distance '+self.current.path+' '+str(dx*(10**9))+' nm'
49 self.outlet.push(to_dump)
52 def do_force(self,args):
56 Measure the force difference (in pN) between two points
60 if self.current.curve.experiment == 'clamp':
61 print 'This command makes no sense for a force clamp experiment.'
63 dx,unitx,dy,unity=self._delta(set=1)
64 print str(dy*(10**12))+' pN'
65 to_dump='force '+self.current.path+' '+str(dy*(10**12))+' pN'
66 self.outlet.push(to_dump)
69 def do_forcebase(self,args):
73 Measures the difference in force (in pN) between a point and a baseline
74 took as the average between two points.
76 The baseline is fixed once for a given curve and different force measurements,
77 unless the user wants it to be recalculated
79 Syntax: forcebase [rebase]
80 rebase: Forces forcebase to ask again the baseline
81 max: Instead of asking for a point to measure, asks for two points and use
82 the maximum peak in between
84 rebase=False #if true=we select rebase
85 maxpoint=False #if true=we measure the maximum peak
87 plot=self._get_displayed_plot()
88 whatset=1 #fixme: for all sets
89 if 'rebase' in args or (self.basecurrent != self.current.path):
95 print 'Select baseline'
96 self.basepoints=self._measure_N_points(N=2, whatset=whatset)
97 self.basecurrent=self.current.path
100 print 'Select two points'
101 points=self._measure_N_points(N=2, whatset=whatset)
102 boundpoints=[points[0].index, points[1].index]
105 y=min(plot.vectors[whatset][1][boundpoints[0]:boundpoints[1]])
107 print 'Chosen interval not valid. Try picking it again. Did you pick the same point as begin and end of interval?'
109 print 'Select point to measure'
110 points=self._measure_N_points(N=1, whatset=whatset)
111 #whatplot=points[0].dest
112 y=points[0].graph_coords[1]
114 #fixme: code duplication
115 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
117 to_average=plot.vectors[whatset][1][boundaries[0]:boundaries[1]] #y points to average
119 avg=np.mean(to_average)
121 print str(forcebase*(10**12))+' pN'
122 to_dump='forcebase '+self.current.path+' '+str(forcebase*(10**12))+' pN'
123 self.outlet.push(to_dump)
125 def plotmanip_multiplier(self, plot, current):
127 Multiplies all the Y values of an SMFS curve by a value stored in the 'force_multiplier'
128 configuration variable. Useful for calibrations and other stuff.
132 if current.curve.experiment != 'smfs':
135 #only one set is present...
136 if len(self.plots[0].vectors) != 2:
140 if (self.config['force_multiplier']==1):
143 for i in range(len(plot.vectors[0][1])):
144 plot.vectors[0][1][i]=plot.vectors[0][1][i]*self.config['force_multiplier']
146 for i in range(len(plot.vectors[1][1])):
147 plot.vectors[1][1][i]=plot.vectors[1][1][i]*self.config['force_multiplier']
152 def plotmanip_flatten(self, plot, current, customvalue=False):
154 Subtracts a polynomial fit to the non-contact part of the curve, as to flatten it.
155 the best polynomial fit is chosen among polynomials of degree 1 to n, where n is
156 given by the configuration file or by the customvalue.
158 customvalue= int (>0) --> starts the function even if config says no (default=False)
162 if current.curve.experiment != 'smfs':
165 #only one set is present...
166 if len(self.plots[0].vectors) != 2:
169 #config is not flatten, and customvalue flag is false too
170 if (not self.config['flatten']) and (not customvalue):
177 max_cycles=customvalue
179 max_cycles=self.config['flatten'] #Using > 1 usually doesn't help and can give artefacts. However, it could be useful too.
181 contact_index=self.find_contact_point()
183 valn=[[] for item in range(max_exponent)]
184 yrn=[0.0 for item in range(max_exponent)]
185 errn=[0.0 for item in range(max_exponent)]
187 #Check if we have a proper numerical value
191 #Loudly and annoyingly complain if it's not a number, then fallback to zero
192 print '''Warning: flatten value is not a number!
193 Use "set flatten" or edit hooke.conf to set it properly
197 for i in range(int(max_cycles)):
199 x_ext=plot.vectors[0][0][contact_index+delta_contact:]
200 y_ext=plot.vectors[0][1][contact_index+delta_contact:]
201 x_ret=plot.vectors[1][0][contact_index+delta_contact:]
202 y_ret=plot.vectors[1][1][contact_index+delta_contact:]
203 for exponent in range(max_exponent):
205 valn[exponent]=sp.polyfit(x_ext,y_ext,exponent)
206 yrn[exponent]=sp.polyval(valn[exponent],x_ret)
207 errn[exponent]=sp.sqrt(sum((yrn[exponent]-y_ext)**2)/float(len(y_ext)))
209 print 'Cannot flatten!'
213 best_exponent=errn.index(min(errn))
216 ycorr_ext=y_ext-yrn[best_exponent]+y_ext[0] #noncontact part
217 yjoin_ext=np.array(plot.vectors[0][1][0:contact_index+delta_contact]) #contact part
219 ycorr_ret=y_ret-yrn[best_exponent]+y_ext[0] #noncontact part
220 yjoin_ret=np.array(plot.vectors[1][1][0:contact_index+delta_contact]) #contact part
222 ycorr_ext=np.concatenate((yjoin_ext, ycorr_ext))
223 ycorr_ret=np.concatenate((yjoin_ret, ycorr_ret))
225 plot.vectors[0][1]=list(ycorr_ext)
226 plot.vectors[1][1]=list(ycorr_ret)
231 def do_slope(self,args):
235 Measures the slope of a delimited chunk on the return trace.
236 The chunk can be delimited either by two manual clicks, or have
237 a fixed width, given as an argument.
239 Syntax: slope [width]
240 The facultative [width] parameter specifies how many
241 points will be considered for the fit. If [width] is
242 specified, only one click will be required.
243 (c) Marco Brucale, Massimo Sandal 2008
246 # Reads the facultative width argument
252 # Decides between the two forms of user input, as per (args)
254 # Gets the Xs of two clicked points as indexes on the current curve vector
255 print 'Click twice to delimit chunk'
256 points=self._measure_N_points(N=2,whatset=1)
258 print 'Click once on the leftmost point of the chunk (i.e.usually the peak)'
259 points=self._measure_N_points(N=1,whatset=1)
261 slope=self._slope(points,fitspan)
263 # Outputs the relevant slope parameter
266 to_dump='slope '+self.current.path+' '+str(slope)
267 self.outlet.push(to_dump)
269 def _slope(self,points,fitspan):
270 # Calls the function linefit_between
271 parameters=[0,0,[],[]]
273 clickedpoints=[points[0].index,points[1].index]
276 clickedpoints=[points[0].index-fitspan,points[0].index]
279 parameters=self.linefit_between(clickedpoints[0],clickedpoints[1])
281 print 'Cannot fit. Did you click twice the same point?'
284 # Outputs the relevant slope parameter
286 print str(parameters[0])
287 to_dump='slope '+self.curve.path+' '+str(parameters[0])
288 self.outlet.push(to_dump)
290 # Makes a vector with the fitted parameters and sends it to the GUI
291 xtoplot=parameters[2]
295 ytoplot.append((x*parameters[0])+parameters[1])
297 clickvector_x, clickvector_y=[], []
299 clickvector_x.append(item.graph_coords[0])
300 clickvector_y.append(item.graph_coords[1])
302 lineplot=self._get_displayed_plot(0) #get topmost displayed plot
304 lineplot.add_set(xtoplot,ytoplot)
305 lineplot.add_set(clickvector_x, clickvector_y)
308 if lineplot.styles==[]:
309 lineplot.styles=[None,None,None,'scatter']
311 lineplot.styles+=[None,'scatter']
312 if lineplot.colors==[]:
313 lineplot.colors=[None,None,'black',None]
315 lineplot.colors+=['black',None]
318 self._send_plot([lineplot])
323 def linefit_between(self,index1,index2,whatset=1):
325 Creates two vectors (xtofit,ytofit) slicing out from the
326 current return trace a portion delimited by the two indexes
328 Then does a least squares linear fit on that slice.
329 Finally returns [0]=the slope, [1]=the intercept of the
330 fitted 1st grade polynomial, and [2,3]=the actual (x,y) vectors
332 (c) Marco Brucale, Massimo Sandal 2008
334 # Translates the indexes into two vectors containing the x,y data to fit
335 xtofit=self.plots[0].vectors[whatset][0][index1:index2]
336 ytofit=self.plots[0].vectors[whatset][1][index1:index2]
338 # Does the actual linear fitting (simple least squares with numpy.polyfit)
340 linefit=np.polyfit(xtofit,ytofit,1)
342 return (linefit[0],linefit[1],xtofit,ytofit)