1 # Copyright (C) 2008-2012 Fabrizio Benedetti <fabrizio.benedetti.82@gmail.com>
2 # Marco Brucale <marco.brucale@unibo.it>
3 # Massimo Sandal <devicerandom@gmail.com>
4 # W. Trevor King <wking@drexel.edu>
6 # This file is part of Hooke.
8 # Hooke is free software: you can redistribute it and/or modify it
9 # under the terms of the GNU Lesser General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
13 # Hooke is distributed in the hope that it will be useful, but WITHOUT
14 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
16 # Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with Hooke. If not, see
20 # <http://www.gnu.org/licenses/>.
22 """The autopeak module provides :class:`Autopeak`, a
23 :class:`hooke.plugin.Plugin` for automatically extracting force peaks
24 (unfolding events) from force curves.
27 from hooke.libhooke import WX_GOOD
30 wxversion.select(WX_GOOD)
31 from wx import PostEvent
39 warnings.simplefilter('ignore',np.RankWarning)
41 #from .. import ui.gui.results as results
44 class autopeakCommands(object):
46 Autopeak carries out force curve fitting with a chosen model:
51 def do_autopeak(self, args):
55 Automatically performs a number of analyses on the peaks of the given curve.
56 Currently it automatically:
57 - fits peaks with WLC or FJC function (depending on how the fit_function variable is set)
58 - measures peak maximum forces with a baseline
59 - measures slope in proximity of peak maximum
60 Requires flatten plotmanipulator , fit.py plugin , flatfilts.py plugin with convfilt
63 autopeak [rebase] [pl=value] [t=value] [noauto] [reclick]
65 rebase : Re-asks baseline interval
67 pl=[value] : Use a fixed persistent length for the fit. If pl is not given,
68 the fit will be a 2-variable
69 fit. DO NOT put spaces between 'pl', '=' and the value.
70 The value must be in meters.
71 Scientific notation like 0.35e-9 is fine.
73 t=[value] : Use a user-defined temperature. The value must be in
74 kelvins; by default it is 293 K.
75 DO NOT put spaces between 't', '=' and the value.
77 noauto : allows for clicking the contact point by
78 hand (otherwise it is automatically estimated) the first time.
79 If subsequent measurements are made, the same contact point
80 clicked the first time is used
82 reclick : redefines by hand the contact point, if noauto has been used before
83 but the user is unsatisfied of the previously choosen contact point.
85 usepoints : fit interval by number of points instead than by nanometers
87 noflatten : does not use the "flatten" plot manipulator
89 When you first issue the command, it will ask for the filename. If you are giving the filename
90 of an existing file, autopeak will resume it and append measurements to it. If you are giving
91 a new filename, it will create the file and append to it until you close Hooke.
94 Useful variables (to set with SET command):
96 fit_function = type of function to use for elasticity. If "wlc" worm-like chain is used, if "fjc" freely jointed
99 temperature= temperature of the system for wlc/fjc fit (in K)
101 auto_slope_span = number of points on which measure the slope, for slope
103 auto_fit_nm = number of nm to fit before the peak maximum, for WLC/FJC (if usepoints false)
104 auto_fit_points = number of points to fit before the peak maximum, for WLC/FJC (if usepoints true)
106 baseline_clicks = -1: no baseline, f=0 at the contact point (whether hand-picked or automatically found)
107 0: automatic baseline
108 1: decide baseline with a single click and length defined in auto_left_baseline
109 2: let user click points of baseline
110 auto_left_baseline = length in nm to use as baseline from the right point (if baseline_clicks=0 , 1)
111 auto_right_baseline = distance in nm of peak-most baseline point from last peak (if baseline_clicks = 0)
113 auto_min_p ; auto_max_p = Minimum and maximum persistence length (if using WLC) or Kuhn length (if using FJC)
114 outside of which the peak is automatically discarded (in nm)
117 #default fit etc. variables
119 T=self.config['temperature']
121 slope_span=int(self.config['auto_slope_span'])
123 rebase=False #if true=we select rebase
124 noflatten=False #if true=we avoid flattening
126 #initialize output data vectors
135 displayed_plot=self._get_displayed_plot(0)
137 #COMMAND LINE PARSING
138 #--Using points instead of nm interval
139 if 'usepoints' in args.split():
140 fit_points=int(self.config['auto_fit_points'])
145 #--Recalculate baseline
146 if 'rebase' in args or (self.basecurrent != self.current.path):
149 if 'noflatten' in args:
152 #--Custom persistent length / custom temperature
153 for arg in args.split():
154 #look for a persistent length argument.
156 pl_expression=arg.split('=')
157 pl_value=float(pl_expression[1]) #actual value
158 #look for a T argument. FIXME: spaces are not allowed between 'pl' and value
159 if ('t=' in arg[0:2]) or ('T=' in arg[0:2]):
160 t_expression=arg.split('=')
161 T=float(t_expression[1])
162 #--Contact point arguments
163 if 'reclick' in args.split():
164 print 'Click contact point'
165 contact_point, contact_point_index = self.pickup_contact_point()
166 elif 'noauto' in args.split():
167 if self.wlccontact_index==None or self.wlccurrent != self.current.path:
168 print 'Click contact point'
169 contact_point , contact_point_index = self.pickup_contact_point()
171 contact_point=self.wlccontact_point
172 contact_point_index=self.wlccontact_index
174 #Automatically find contact point
175 cindex=self.find_contact_point()
176 contact_point=self._clickize(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1], cindex)
177 #--END COMMAND LINE PARSING--
180 peak_location, peak_size = self.find_current_peaks(noflatten)
182 if len(peak_location) == 0:
183 print 'No peaks to fit.'
186 fitplot=copy.deepcopy(displayed_plot)
188 #Pick up force baseline
190 self.basepoints=self.baseline_points(peak_location, displayed_plot)
192 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
194 to_average=displayed_plot.vectors[1][1][boundaries[0]:boundaries[1]] #y points to average
195 avg=np.mean(to_average)
197 clicks=self.config['baseline_clicks']
200 avg=displayed_plot.vectors[1][1][contact_point_index]
202 avg=displayed_plot.vectors[1][1][cindex]
204 for peak in peak_location:
208 fit_points=self.fit_interval_nm(peak, displayed_plot, self.config['auto_fit_nm'], True)
209 peak_point=self._clickize(displayed_plot.vectors[1][0],displayed_plot.vectors[1][1],peak)
210 other_fit_point=self._clickize(displayed_plot.vectors[1][0],displayed_plot.vectors[1][1],peak-fit_points)
213 points=[contact_point, peak_point, other_fit_point]
215 if abs(peak_point.index-other_fit_point.index) < 2:
218 if self.config['fit_function']=='wlc':
220 params, yfit, xfit, fit_errors = self.wlc_fit(points, displayed_plot.vectors[1][0], displayed_plot.vectors[1][1], pl_value, T, return_errors=True)
221 elif self.config['fit_function']=='fjc':
222 params, yfit, xfit, fit_errors = self.fjc_fit(points, displayed_plot.vectors[1][0], displayed_plot.vectors[1][1], pl_value, T, return_errors=True)
224 print 'Unknown fit function'
225 print 'Please set fit_function as wlc or fjc'
230 delta_to_measure=displayed_plot.vectors[1][1][peak-delta_force:peak+delta_force]
231 y=min(delta_to_measure)
232 #save force values (pN)
234 slope=self.linefit_between(peak-slope_span,peak)[0]
237 #check fitted data and, if right, add peak to the measurement
238 #FIXME: code duplication
239 if len(params)==1: #if we did choose 1-value fit
240 p_lengths.append(pl_value)
241 c_lengths.append(params[0]*(1.0e+9))
242 sigma_p_lengths.append(0)
243 sigma_c_lengths.append(fit_errors[0]*(1.0e+9))
244 forces.append(abs(y-avg)*(1.0e+12))
246 #Add WLC fit lines to plot
247 fitplot.add_set(xfit,yfit)
248 if len(fitplot.styles)==0:
252 fitplot.styles.append(None)
253 fitplot.colors.append(None)
255 p_leng=params[1]*(1.0e+9)
256 #check if persistent length makes sense. otherwise, discard peak.
257 if p_leng>self.config['auto_min_p'] and p_leng<self.config['auto_max_p']:
258 p_lengths.append(p_leng)
259 c_lengths.append(params[0]*(1.0e+9))
260 sigma_c_lengths.append(fit_errors[0]*(1.0e+9))
261 sigma_p_lengths.append(fit_errors[1]*(1.0e+9))
262 forces.append(abs(y-avg)*(1.0e+12))
265 #Add WLC fit lines to plot
266 fitplot.add_set(xfit,yfit)
267 if len(fitplot.styles)==0:
271 fitplot.styles.append(None)
272 fitplot.colors.append(None)
277 #add basepoints to fitplot
278 fitplot.add_set([self.basepoints[0].graph_coords[0],self.basepoints[1].graph_coords[0]],[self.basepoints[0].graph_coords[1],self.basepoints[1].graph_coords[1]])
279 fitplot.styles.append('scatter')
280 fitplot.colors.append(None)
282 #Show wlc fits and peak locations
283 self._send_plot([fitplot])
285 print 'Using fit function: ',self.config['fit_function']
286 print 'Measurements for all peaks detected:'
287 print 'contour (nm)', c_lengths
288 print 'sigma contour (nm)',sigma_c_lengths
289 print 'p (nm)',p_lengths
290 print 'sigma p (nm)',sigma_p_lengths
291 print 'forces (pN)',forces
292 print 'slopes (N/m)',slopes
295 while controller==False:
296 #Ask the user what peaks to ignore from analysis.
297 print 'Peaks to ignore (0,1...n from contact point,return to take all)'
298 print 'N to discard measurement'
299 exclude_raw=raw_input('Input:')
305 if not exclude_raw=='':
306 exclude=exclude_raw.split(',')
308 exclude=[int(item) for item in exclude]
314 sigma_c_lengths[i]=None
315 sigma_p_lengths[i]=None
321 #Clean data vectors from ignored peaks
322 #FIXME:code duplication
323 c_lengths=[item for item in c_lengths if item != None]
324 p_lengths=[item for item in p_lengths if item != None]
325 forces=[item for item in forces if item != None]
326 slopes=[item for item in slopes if item != None]
327 sigma_c_lengths=[item for item in sigma_c_lengths if item != None]
328 sigma_p_lengths=[item for item in sigma_p_lengths if item != None]
330 print 'Measurements for chosen peaks:'
331 print 'contour (nm)',c_lengths
332 print 'sigma contour (nm)',sigma_c_lengths
333 print 'p (nm)',p_lengths
334 print 'sigma p (nm)',sigma_p_lengths
335 print 'forces (pN)',forces
336 print 'slopes (N/m)',slopes
339 if self.autofile=='':
340 self.autofile=raw_input('Autopeak filename? (return to ignore) ')
341 if self.autofile=='':
345 if not os.path.exists(self.autofile):
346 f=open(self.autofile,'w+')
347 f.write('Analysis started '+time.asctime()+'\n')
348 f.write('----------------------------------------\n')
349 f.write('; Contour length (nm) ; Persistence length (nm) ; Max.Force (pN) ; Slope (N/m) ; Sigma contour (nm) ; Sigma persistence (nm)\n')
353 f=open(self.autofile,'a+')
355 f.write(self.current.path+'\n')
356 for i in range(len(c_lengths)):
357 f.write(' ; '+str(c_lengths[i])+' ; '+str(p_lengths[i])+' ; '+str(forces[i])+' ; '+str(slopes[i])+' ; '+str(sigma_c_lengths[i])+' ; '+str(sigma_p_lengths[i])+'\n')
360 self.do_note('autopeak')