2 # -*- coding: utf-8 -*-
7 Force spectroscopy curves basic fitting plugin.
8 Licensed under the GNU GPL version 2
10 Non-standard Dependencies:
11 procplots.py (plot processing plugin)
13 from libhooke import WX_GOOD, ClickedPoint
15 wxversion.select(WX_GOOD)
16 #from wx import PostEvent
17 #from wx.lib.newevent import NewEvent
25 global EVT_MEASURE_WLC
27 #measure_wlc, EVT_MEASURE_WLC = NewEvent()
29 global events_from_fit
30 events_from_fit=Queue.Queue() #GUI ---> CLI COMMUNICATION
37 self.wlccontact_point=None
38 self.wlccontact_index=None
41 '''Calculates the average distance from data to fit, scaled by the standard deviation
42 of the free cantilever area (thermal noise)
47 def wlc_fit(self,clicked_points,xvector,yvector, pl_value, T=293, return_errors=False):
49 Worm-like chain model fitting.
50 The function is the simple polynomial worm-like chain as proposed by C.Bustamante, J.F.Marko, E.D.Siggia
51 and S.Smith (Science. 1994 Sep 9;265(5178):1599-600.)
55 clicked_points[0] = contact point (calculated or hand-clicked)
56 clicked_points[1] and [2] are edges of chunk
59 #STEP 1: Prepare the vectors to apply the fit.
60 if pl_value is not None:
61 pl_value=pl_value/(10**9)
63 #indexes of the selected chunk
64 first_index=min(clicked_points[1].index, clicked_points[2].index)
65 last_index=max(clicked_points[1].index, clicked_points[2].index)
67 #getting the chunk and reverting it
68 xchunk,ychunk=xvector[first_index:last_index],yvector[first_index:last_index]
71 #put contact point at zero and flip around the contact point (the fit wants a positive growth for extension and force)
72 xchunk_corr_up=[-(x-clicked_points[0].graph_coords[0]) for x in xchunk]
73 ychunk_corr_up=[-(y-clicked_points[0].graph_coords[1]) for y in ychunk]
76 xchunk_corr_up=scipy.array(xchunk_corr_up)
77 ychunk_corr_up=scipy.array(ychunk_corr_up)
80 #STEP 2: actually do the fit
82 #Find furthest point of chunk and add it a bit; the fit must converge
84 xchunk_high=max(xchunk_corr_up)
85 xchunk_high+=(xchunk_high/10)
87 #Here are the linearized start parameters for the WLC.
88 #[lambd=1/Lo , pii=1/P]
90 p0=[(1/xchunk_high),(1/(3.5e-10))]
91 p0_plfix=[(1/xchunk_high)]
94 fixme: remove these comments after testing
96 def dist(px,py,linex,liney):
97 distancesx=scipy.array([(px-x)**2 for x in linex])
98 minindex=np.argmin(distancesx)
99 print px, linex[0], linex[-1]
100 return (py-liney[minindex])**2
102 def f_wlc(params,x,T=T):
104 wlc function for ODR fitting
109 y=(therm*pii/4.0) * (((1-(x*lambd))**-2) - 1 + (4*x*lambd))
112 def f_wlc_plfix(params,x,pl_value=pl_value,T=T):
114 wlc function for ODR fitting
120 y=(therm*pii/4.0) * (((1-(x*lambd))**-2) - 1 + (4*x*lambd))
124 realdata=scipy.odr.RealData(xchunk_corr_up,ychunk_corr_up)
126 model=scipy.odr.Model(f_wlc_plfix)
127 o = scipy.odr.ODR(realdata, model, p0_plfix)
129 model=scipy.odr.Model(f_wlc)
130 o = scipy.odr.ODR(realdata, model, p0)
132 o.set_job(fit_type=2)
134 fit_out=[(1/i) for i in out.beta]
136 #Calculate fit errors from output standard deviations.
137 #We must propagate the error because we fit the *inverse* parameters!
138 #The error = (error of the inverse)*(value**2)
140 for sd,value in zip(out.sd_beta, fit_out):
141 err_real=sd*(value**2)
142 fit_errors.append(err_real)
144 def wlc_eval(x,params,pl_value,T):
146 Evaluates the WLC function
156 Kb=(1.38065e-23) #boltzmann constant
157 therm=Kb*T #so we have thermal energy
159 return ( (therm*pii/4.0) * (((1-(x*lambd))**-2.0) - 1 + (4.0*x*lambd)) )
162 #STEP 3: plotting the fit
164 #obtain domain to plot the fit - from contact point to last_index plus 20 points
165 thule_index=last_index+10
166 if thule_index > len(xvector): #for rare cases in which we fit something at the END of whole curve.
167 thule_index = len(xvector)
168 #reverse etc. the domain
169 xfit_chunk=xvector[clicked_points[0].index:thule_index]
171 xfit_chunk_corr_up=[-(x-clicked_points[0].graph_coords[0]) for x in xfit_chunk]
172 xfit_chunk_corr_up=scipy.array(xfit_chunk_corr_up)
174 #the fitted curve: reflip, re-uncorrect
175 yfit=wlc_eval(xfit_chunk_corr_up, out.beta, pl_value,T)
176 yfit_down=[-y for y in yfit]
177 yfit_corr_down=[y+clicked_points[0].graph_coords[1] for y in yfit_down]
180 #calculate fit quality
182 yqeval=wlc_eval(xchunk_corr_up,out.beta,pl_value,T)
183 #we need to cut the extra from thuleindex
184 for qindex in np.arange(0,len(yqeval)):
185 qsum+=(yqeval[qindex]-ychunk_corr_up[qindex])**2
186 qstd=np.sqrt(qsum/len(ychunk_corr_up))
190 return fit_out, yfit_corr_down, xfit_chunk, fit_errors, qstd
192 return fit_out, yfit_corr_down, xfit_chunk, None, qstd
194 def fjc_fit(self,clicked_points,xvector,yvector, pl_value, T=293, return_errors=False):
196 Freely-jointed chain function
197 ref: C.Ray and B.B. Akhremitchev; http://www.chem.duke.edu/~boris/research/force_spectroscopy/fit_efjc.pdf
200 clicked_points[0] is the contact point (calculated or hand-clicked)
201 clicked_points[1] and [2] are edges of chunk
204 #STEP 1: Prepare the vectors to apply the fit.
205 if pl_value is not None:
206 pl_value=pl_value/(10**9)
208 #indexes of the selected chunk
209 first_index=min(clicked_points[1].index, clicked_points[2].index)
210 last_index=max(clicked_points[1].index, clicked_points[2].index)
212 #getting the chunk and reverting it
213 xchunk,ychunk=xvector[first_index:last_index],yvector[first_index:last_index]
216 #put contact point at zero and flip around the contact point (the fit wants a positive growth for extension and force)
217 xchunk_corr_up=[-(x-clicked_points[0].graph_coords[0]) for x in xchunk]
218 ychunk_corr_up=[-(y-clicked_points[0].graph_coords[1]) for y in ychunk]
222 xchunk_corr_up=scipy.array(xchunk_corr_up)
223 ychunk_corr_up=scipy.array(ychunk_corr_up)
226 #STEP 2: actually do the fit
228 #Find furthest point of chunk and add it a bit; the fit must converge
230 xchunk_high=max(xchunk_corr_up)
231 xchunk_high+=(xchunk_high/10)
233 #Here are the linearized start parameters for the WLC.
234 #[lambd=1/Lo , pii=1/P]
236 p0=[(1/xchunk_high),(1/(3.5e-10))]
237 p0_plfix=[(1/xchunk_high)]
240 fixme: remove these comments after testing
242 def dist(px,py,linex,liney):
243 distancesx=scipy.array([(px-x)**2 for x in linex])
244 minindex=np.argmin(distancesx)
245 print minindex, px, linex[0], linex[-1]
246 return (py-liney[minindex])**2
252 return (np.exp(2*z)+1)/(np.exp(2*z)-1)
254 def x_fjc(params,f,T=T):
256 fjc function for ODR fitting
262 #x=(therm*pii/4.0) * (((1-(x*lambd))**-2) - 1 + (4*x*lambd))
263 x=(1/lambd)*(coth(f*(1/pii)/therm) - (therm*pii)/f)
266 def x_fjc_plfix(params,f,pl_value=pl_value,T=T):
268 fjc function for ODR fitting
274 #y=(therm*pii/4.0) * (((1-(x*lambd))**-2) - 1 + (4*x*lambd))
275 x=(1/lambd)*(coth(f*(1/pii)/therm) - (therm*pii)/f)
279 realdata=scipy.odr.RealData(ychunk_corr_up,xchunk_corr_up)
281 model=scipy.odr.Model(x_fjc_plfix)
282 o = scipy.odr.ODR(realdata, model, p0_plfix)
284 model=scipy.odr.Model(x_fjc)
285 o = scipy.odr.ODR(realdata, model, p0)
287 o.set_job(fit_type=2)
289 fit_out=[(1/i) for i in out.beta]
291 #Calculate fit errors from output standard deviations.
292 #We must propagate the error because we fit the *inverse* parameters!
293 #The error = (error of the inverse)*(value**2)
295 for sd,value in zip(out.sd_beta, fit_out):
296 err_real=sd*(value**2)
297 fit_errors.append(err_real)
299 def fjc_eval(y,params,pl_value,T):
301 Evaluates the WLC function
311 Kb=(1.38065e-23) #boltzmann constant
312 therm=Kb*T #so we have thermal energy
313 #return ( (therm*pii/4.0) * (((1-(x*lambd))**-2.0) - 1 + (4.0*x*lambd)) )
314 return (1/lambd)*(coth(y*(1/pii)/therm) - (therm*pii)/y)
317 #STEP 3: plotting the fit
318 #obtain domain to plot the fit - from contact point to last_index plus 20 points
319 thule_index=last_index+10
320 if thule_index > len(xvector): #for rare cases in which we fit something at the END of whole curve.
321 thule_index = len(xvector)
322 #reverse etc. the domain
323 ychunk=yvector[clicked_points[0].index:thule_index]
326 y_evalchunk=np.linspace(min(ychunk),max(ychunk),100)
328 #Empty y-chunk. It happens whenever we set the contact point after a recognized peak,
329 #or other buggy situations. Kludge to live with it now...
330 ychunk=yvector[:thule_index]
331 y_evalchunk=np.linspace(min(ychunk),max(ychunk),100)
333 yfit_down=[-y for y in y_evalchunk]
334 yfit_corr_down=[y+clicked_points[0].graph_coords[1] for y in yfit_down]
335 yfit_corr_down=scipy.array(yfit_corr_down)
337 #the fitted curve: reflip, re-uncorrect
338 xfit=fjc_eval(yfit_corr_down, out.beta, pl_value,T)
341 xfit_chunk_corr_up=[-(x-clicked_points[0].graph_coords[0]) for x in xfit]
343 #xfit_chunk_corr_up=scipy.array(xfit_chunk_corr_up)
344 #deltay=yfit_down[0]-yvector[clicked_points[0].index]
346 #This is a terrible, terrible kludge to find the point where it should normalize (and from where it should plot)
348 for index in scipy.arange(1,len(xfit_chunk_corr_up),1):
349 xxxdists.append((clicked_points[0].graph_coords[0]-xfit_chunk_corr_up[index])**2)
350 normalize_index=xxxdists.index(min(xxxdists))
353 deltay=yfit_down[normalize_index]-clicked_points[0].graph_coords[1]
354 yfit_corr_down=[y-deltay for y in yfit_down]
357 #calculate fit quality
358 #creates dense y vector
359 yqeval=np.linspace(np.min(ychunk_corr_up)/2,np.max(ychunk_corr_up)*2,10*len(ychunk_corr_up))
360 #corresponding fitted x
361 xqeval=fjc_eval(yqeval,out.beta,pl_value,T)
364 for qindex in np.arange(0,len(ychunk_corr_up)):
365 qsum+=dist(xchunk_corr_up[qindex],ychunk_corr_up[qindex],xqeval,yqeval)
366 qstd=np.sqrt(qsum/len(ychunk_corr_up))
370 return fit_out, yfit_corr_down[normalize_index+1:], xfit_chunk_corr_up[normalize_index+1:], fit_errors, qstd
372 return fit_out, yfit_corr_down[normalize_index+1:], xfit_chunk_corr_up[normalize_index+1:], None, qstd
374 def efjc_fit(self,clicked_points,xvector,yvector, pl_value, T=293.0, return_errors=False):
376 Extended Freely-jointed chain function
377 ref: F Oesterhelt, M Rief and H E Gaub, New Journal of Physics 1 (1999) 6.1–6.11
378 Please note that 2-parameter fitting (length and kl) usually does not converge, use fixed kl
381 clicked_points[0] is the contact point (calculated or hand-clicked)
382 clicked_points[1] and [2] are edges of chunk
385 #Fixed parameters from reference
386 Kb=(1.38065e-2) #in pN.nm
388 Lh=0.280 #helical, nm
392 #STEP 1: Prepare the vectors to apply the fit.
394 #indexes of the selected chunk
395 first_index=min(clicked_points[1].index, clicked_points[2].index)
396 last_index=max(clicked_points[1].index, clicked_points[2].index)
398 #getting the chunk and reverting it
399 xchunk,ychunk=xvector[first_index:last_index],yvector[first_index:last_index]
402 #put contact point at zero and flip around the contact point (the fit wants a positive growth for extension and force)
403 xchunk_corr_up=[-(x-clicked_points[0].graph_coords[0]) for x in xchunk]
404 ychunk_corr_up=[-(y-clicked_points[0].graph_coords[1]) for y in ychunk]
408 xchunk_corr_up=scipy.array(xchunk_corr_up)
409 ychunk_corr_up=scipy.array(ychunk_corr_up)
411 xchunk_corr_up_nm=xchunk_corr_up*1e9
412 ychunk_corr_up_pn=ychunk_corr_up*1e12
415 #STEP 2: actually do the fit
417 #Find furthest point of chunk and add it a bit; the fit must converge
419 xchunk_high=max(xchunk_corr_up_nm)
420 xchunk_high+=(xchunk_high/10.0)
422 #Here are the linearized start parameters for the WLC.
424 #max number of monomers (all helical)for a contour length xchunk_high
425 excessNs=xchunk_high/(Lp)
426 p0=[excessNs,(1.0/(0.7))]
427 p0_plfix=[(excessNs)]
429 def dist(px,py,linex,liney):
430 distancesx=scipy.array([(px-x)**2 for x in linex])
431 minindex=np.argmin(distancesx)
432 return (py-liney[minindex])**2
435 dG0=12.4242 #3kt in pN.nm
436 dL=0.078 #planar-helical
441 Lh=0.280 #helical, nm
446 return Lp/(np.exp(dG/therm)+1)+Lh/(np.exp(-dG/therm)+1)
452 return 1.0/np.tanh(z)
454 def x_efjc(params,f,T=T,Ks=Ks):
456 efjc function for ODR fitting
465 x=Ns*Lfactor(f)*(coth(beta)-1.0/beta)+Ns*f/Ks
468 def x_efjc_plfix(params,f,kl_value=pl_value,T=T,Ks=Ks):
470 efjc function for ODR fitting
479 x=Ns*Lfactor(f)*(coth(beta)-1.0/beta)+Ns*f/Ks
483 realdata=scipy.odr.RealData(ychunk_corr_up_pn,xchunk_corr_up_nm)
485 model=scipy.odr.Model(x_efjc_plfix)
486 o = scipy.odr.ODR(realdata, model, p0_plfix)
488 print 'WARNING eFJC fit with free pl sometimes does not converge'
489 model=scipy.odr.Model(x_efjc)
490 o = scipy.odr.ODR(realdata, model, p0)
492 o.set_job(fit_type=2)
499 kfit=1e-9/out.beta[1]
507 #Calculate fit errors from output standard deviations.
509 fit_errors.append(out.sd_beta[0]*Lp*1e-9)
511 fit_errors.append(1e9*out.sd_beta[1]*kfit**2)
515 def efjc_eval(y,params,pl_value,T=T,Lfactor=Lfactor,Ks=Ks):
517 Evaluates the eFJC function
527 Kb=(1.38065e-2) #boltzmann constant
528 therm=Kb*T #so we have thermal energy
531 x= Ns*Lfactor(y)*(coth(beta)-1.0/beta)+Ns*y/Ks
535 #STEP 3: plotting the fit
536 #obtain domain to plot the fit - from contact point to last_index plus 20 points
537 thule_index=last_index+10
538 if thule_index > len(xvector): #for rare cases in which we fit something at the END of whole curve.
539 thule_index = len(xvector)
540 #reverse etc. the domain
541 ychunk=yvector[clicked_points[0].index:thule_index]
544 y_evalchunk=np.linspace(min(ychunk),max(ychunk),100)
546 #Empty y-chunk. It happens whenever we set the contact point after a recognized peak,
547 #or other buggy situations. Kludge to live with it now...
548 ychunk=yvector[:thule_index]
549 y_evalchunk=np.linspace(min(ychunk),max(ychunk),100)
551 yfit_down=[-y for y in y_evalchunk]
552 yfit_corr_down=[y+clicked_points[0].graph_coords[1] for y in yfit_down]
553 yfit_corr_down=scipy.array(yfit_corr_down)
555 #the fitted curve: reflip, re-uncorrect
556 xfit=efjc_eval(1e12*yfit_corr_down, out.beta, pl_value,T)*1e-9
559 xfit_chunk_corr_up=[-(x-clicked_points[0].graph_coords[0]) for x in xfit]
561 #xfit_chunk_corr_up=scipy.array(xfit_chunk_corr_up)
562 #deltay=yfit_down[0]-yvector[clicked_points[0].index]
564 #This is a terrible, terrible kludge to find the point where it should normalize (and from where it should plot)
566 for index in scipy.arange(1,len(xfit_chunk_corr_up),1):
567 xxxdists.append((clicked_points[0].graph_coords[0]-xfit_chunk_corr_up[index])**2)
568 normalize_index=xxxdists.index(min(xxxdists))
571 deltay=yfit_down[normalize_index]-clicked_points[0].graph_coords[1]
572 yfit_corr_down=[y-deltay for y in yfit_down]
574 #calculate fit quality
575 #creates dense y vector
576 yqeval=np.linspace(np.min(ychunk_corr_up_pn)/2,np.max(ychunk_corr_up_pn)*2,10*len(ychunk_corr_up_pn))
577 #corresponding fitted x
578 xqeval=efjc_eval(yqeval,out.beta,pl_value)
581 for qindex in np.arange(0,len(ychunk_corr_up_pn)):
582 qsum+=dist(xchunk_corr_up_nm[qindex],ychunk_corr_up_pn[qindex],xqeval,yqeval)
583 qstd=1e-12*np.sqrt(qsum/len(ychunk_corr_up_pn))
586 return fit_out, yfit_corr_down[normalize_index+1:], xfit_chunk_corr_up[normalize_index+1:], fit_errors, qstd
588 return fit_out, yfit_corr_down[normalize_index+1:], xfit_chunk_corr_up[normalize_index+1:], None, qstd
593 def do_wlc(self,args):
602 def do_fjc(self,args):
611 def do_fit(self,args):
615 Fits an entropic elasticity function to a given chunk of the curve.
617 First you have to click a contact point.
618 Then you have to click the two edges of the data you want to fit.
620 Fit quality compares the distance to the fit with the thermal noise (a good fit should be close to 1)
622 The fit function depends on the fit_function variable. You can set it with the command
623 "set fit_function wlc" or "set fit_function fjc" depending on the function you prefer.
625 For WLC, the function is the simple polynomial worm-like chain as proposed by
626 C.Bustamante, J.F.Marko, E.D.Siggia and S.Smith (Science. 1994
627 Sep 9;265(5178):1599-600.)
630 C.Ray and B.B. Akhremitchev; http://www.chem.duke.edu/~boris/research/force_spectroscopy/fit_efjc.pdf
633 F Oesterhelt, M Rief and H E Gaub, New Journal of Physics 1 (1999) 6.1–6.11 (section 4.2)
634 NOTE: use fixed pl for better results.
637 pl=[value] : Use a fixed persistent length (WLC) or Kuhn length (FJC) for the fit. If pl is not given,
638 the fit will be a 2-variable
639 fit. DO NOT put spaces between 'pl', '=' and the value.
640 The value must be in nanometers.
642 t=[value] : Use a user-defined temperature. The value must be in
643 kelvins; by default it is 293 K.
644 DO NOT put spaces between 't', '=' and the value.
646 noauto : allows for clicking the contact point by
647 hand (otherwise it is automatically estimated) the first time.
648 If subsequent measurements are made, the same contact point
651 reclick : redefines by hand the contact point, if noauto has been used before
652 but the user is unsatisfied of the previously choosen contact point.
654 Syntax: fit [pl=(value)] [t=value] [noauto]
657 T=self.config['temperature']
658 for arg in args.split():
659 #look for a persistent length argument.
661 pl_expression=arg.split('=')
662 pl_value=float(pl_expression[1]) #actual value
663 #look for a T argument. FIXME: spaces are not allowed between 'pl' and value
664 if ('t=' in arg[0:2]) or ('T=' in arg[0:2]):
665 t_expression=arg.split('=')
666 T=float(t_expression[1])
668 #use the currently displayed plot for the fit
669 displayed_plot=self._get_displayed_plot()
671 #handle contact point arguments correctly
672 if 'reclick' in args.split():
673 print 'Click contact point'
674 contact_point=self._measure_N_points(N=1, whatset=1)[0]
675 contact_point_index=contact_point.index
676 self.wlccontact_point=contact_point
677 self.wlccontact_index=contact_point.index
678 self.wlccurrent=self.current.path
679 elif 'noauto' in args.split():
680 if self.wlccontact_index==None or self.wlccurrent != self.current.path:
681 print 'Click contact point'
682 contact_point=self._measure_N_points(N=1, whatset=1)[0]
683 contact_point_index=contact_point.index
684 self.wlccontact_point=contact_point
685 self.wlccontact_index=contact_point.index
686 self.wlccurrent=self.current.path
688 contact_point=self.wlccontact_point
689 contact_point_index=self.wlccontact_index
691 cindex=self.find_contact_point()
692 contact_point=ClickedPoint()
693 contact_point.absolute_coords=displayed_plot.vectors[1][0][cindex], displayed_plot.vectors[1][1][cindex]
694 contact_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
695 contact_point.is_marker=True
697 print 'Click edges of chunk'
698 points=self._measure_N_points(N=2, whatset=1)
699 points=[contact_point]+points
702 if self.config['fit_function']=='wlc':
703 params, yfit, xfit, fit_errors,qstd = self.wlc_fit(points, displayed_plot.vectors[1][0], displayed_plot.vectors[1][1],pl_value,T, return_errors=True )
704 name_of_charlength='Persistent length'
705 elif self.config['fit_function']=='fjc':
706 params, yfit, xfit, fit_errors,qstd = self.fjc_fit(points, displayed_plot.vectors[1][0], displayed_plot.vectors[1][1],pl_value,T, return_errors=True )
707 name_of_charlength='Kuhn length'
708 elif self.config['fit_function']=='efjc':
709 params, yfit, xfit, fit_errors,qstd = self.efjc_fit(points, displayed_plot.vectors[1][0], displayed_plot.vectors[1][1],pl_value,T, return_errors=True )
710 name_of_charlength='Kuhn length (e)'
712 print 'No recognized fit function defined!'
713 print 'Set your fit function to wlc, fjc or efjc.'
717 print 'Fit not possible. Probably wrong interval -did you click two *different* points?'
720 #FIXME: print "Kuhn length" for FJC
721 print 'Fit function:',self.config['fit_function']
722 print 'Contour length: ',params[0]*(1.0e+9),' nm'
723 to_dump='contour '+self.current.path+' '+str(params[0]*(1.0e+9))+' nm'
724 self.outlet.push(to_dump)
725 if len(params)==2: #if we did choose 2-value fit
726 print name_of_charlength+': ',params[1]*(1.0e+9),' nm'
727 to_dump='persistent '+self.current.path+' '+str(params[1]*(1.0e+9))+' nm'
728 self.outlet.push(to_dump)
731 fit_nm=[i*(10**9) for i in fit_errors]
732 print 'Standard deviation (contour length)', fit_nm[0]
734 print 'Standard deviation ('+name_of_charlength+')', fit_nm[1]
736 print 'Fit quality: '+str(qstd/np.std(displayed_plot.vectors[1][1][-20:-1]))
739 #add the clicked points in the final PlotObject
740 clickvector_x, clickvector_y=[], []
742 clickvector_x.append(item.graph_coords[0])
743 clickvector_y.append(item.graph_coords[1])
745 #create a custom PlotObject to gracefully plot the fit along the curves
747 fitplot=copy.deepcopy(displayed_plot)
748 fitplot.add_set(xfit,yfit)
749 fitplot.add_set(clickvector_x,clickvector_y)
751 #FIXME: this colour/styles stuff must be solved at the root!
752 if fitplot.styles==[]:
753 fitplot.styles=[None,None,None,'scatter']
755 fitplot.styles+=[None,'scatter']
757 if fitplot.colors==[]:
758 fitplot.colors=[None,None,None,None]
760 fitplot.colors+=[None,None]
762 self._send_plot([fitplot])
769 def find_contact_point(self,plot=False):
771 Finds the contact point on the curve.
773 The current algorithm (thanks to Francesco Musiani, francesco.musiani@unibo.it and Massimo Sandal) is:
774 - take care of the PicoForce trigger bug - exclude retraction portions with too high standard deviation
775 - fit the second half of the retraction curve to a line
776 - if the fit is not almost horizontal, take a smaller chunk and repeat
777 - otherwise, we have something horizontal
778 - so take the average of horizontal points and use it as a baseline
780 Then, start from the rise of the retraction curve and look at the first point below the
783 FIXME: should be moved, probably to generalvclamp.py
789 outplot=self.subtract_curves(1)
790 xret=outplot.vectors[1][0]
791 ydiff=outplot.vectors[1][1]
793 xext=plot.vectors[0][0]
794 yext=plot.vectors[0][1]
795 xret2=plot.vectors[1][0]
796 yret=plot.vectors[1][1]
798 #taking care of the picoforce trigger bug: we exclude portions of the curve that have too much
799 #standard deviation. yes, a lot of magic is here.
801 monlength=len(xret)-int(len(xret)/20)
804 monchunk=scipy.array(ydiff[monlength:finalength])
805 if abs(np.std(monchunk)) < 2e-10:
807 else: #move away from the monster
808 monlength-=int(len(xret)/50)
809 finalength-=int(len(xret)/50)
812 #take half of the thing
813 endlength=int(len(xret)/2)
818 xchunk=yext[endlength:monlength]
819 ychunk=yext[endlength:monlength]
820 regr=scipy.stats.linregress(xchunk,ychunk)[0:2]
821 #we stop if we found an almost-horizontal fit or if we're going too short...
822 #FIXME: 0.1 and 6 here are "magic numbers" (although reasonable)
823 if (abs(regr[1]) > 0.1) and ( endlength < len(xret)-int(len(xret)/6) ) :
829 ymean=np.mean(ychunk) #baseline
834 #find the first point below the calculated baseline
840 #The algorithm didn't find anything below the baseline! It should NEVER happen
848 def find_contact_point2(self, debug=False):
850 TO BE DEVELOPED IN THE FUTURE
851 Finds the contact point on the curve.
853 FIXME: should be moved, probably to generalvclamp.py
856 #raw_plot=self.current.curve.default_plots()[0]
857 raw_plot=self.plots[0]
858 '''xext=self.plots[0].vectors[0][0]
859 yext=self.plots[0].vectors[0][1]
860 xret2=self.plots[0].vectors[1][0]
861 yret=self.plots[0].vectors[1][1]
863 xext=raw_plot.vectors[0][0]
864 yext=raw_plot.vectors[0][1]
865 xret2=raw_plot.vectors[1][0]
866 yret=raw_plot.vectors[1][1]
868 first_point=[xext[0], yext[0]]
869 last_point=[xext[-1], yext[-1]]
871 #regr=scipy.polyfit(first_point, last_point,1)[0:2]
872 diffx=abs(first_point[0]-last_point[0])
873 diffy=abs(first_point[1]-last_point[1])
875 #using polyfit results in numerical errors. good old algebra.
877 b=first_point[1]-(a*first_point[0])
878 baseline=scipy.polyval((a,b), xext)
880 ysub=[item-basitem for item,basitem in zip(yext,baseline)]
882 contact=ysub.index(min(ysub))
884 return xext,ysub,contact
886 #now, exploit a ClickedPoint instance to calculate index...
888 dummy.absolute_coords=(x_intercept,y_intercept)
889 dummy.find_graph_coords(xret2,yret)
892 return dummy.index, regr, regr_contact
898 def x_do_contact(self,args):
900 DEBUG COMMAND to be activated in the future
902 xext,ysub,contact=self.find_contact_point2(debug=True)
904 contact_plot=self.plots[0]
905 contact_plot.add_set(xext,ysub)
906 contact_plot.add_set([xext[contact]],[self.plots[0].vectors[0][1][contact]])
907 #contact_plot.add_set([first_point[0]],[first_point[1]])
908 #contact_plot.add_set([last_point[0]],[last_point[1]])
909 contact_plot.styles=[None,None,None,'scatter']
910 self._send_plot([contact_plot])
914 index,regr,regr_contact=self.find_contact_point2(debug=True)
917 raw_plot=self.current.curve.default_plots()[0]
918 xret=raw_plot.vectors[0][0]
919 #nc_line=[(item*regr[0])+regr[1] for item in x_nc]
920 nc_line=scipy.polyval(regr,xret)
921 c_line=scipy.polyval(regr_contact,xret)
924 contact_plot=self.current.curve.default_plots()[0]
925 contact_plot.add_set(xret, nc_line)
926 contact_plot.add_set(xret, c_line)
927 contact_plot.styles=[None,None,None,None]
928 #contact_plot.styles.append(None)
929 contact_plot.destination=1
930 self._send_plot([contact_plot])