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@tremily.us>
6 # This file is part of Hooke.
8 # Hooke is free software: you can redistribute it and/or modify it under the
9 # terms of the GNU Lesser General Public License as published by the Free
10 # Software Foundation, either version 3 of the License, or (at your option) any
13 # Hooke is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18 # You should have received a copy of the GNU Lesser General Public License
19 # along with Hooke. If not, see <http://www.gnu.org/licenses/>.
21 """The autopeak module provides :class:`Autopeak`, a
22 :class:`hooke.plugin.Plugin` for automatically extracting force peaks
23 (unfolding events) from force curves.
26 from hooke.libhooke import WX_GOOD
29 wxversion.select(WX_GOOD)
30 from wx import PostEvent
38 warnings.simplefilter('ignore',np.RankWarning)
40 #from .. import ui.gui.results as results
43 class autopeakCommands(object):
45 Autopeak carries out force curve fitting with a chosen model:
50 def do_autopeak(self, args):
54 Automatically performs a number of analyses on the peaks of the given curve.
55 Currently it automatically:
56 - fits peaks with WLC or FJC function (depending on how the fit_function variable is set)
57 - measures peak maximum forces with a baseline
58 - measures slope in proximity of peak maximum
59 Requires flatten plotmanipulator , fit.py plugin , flatfilts.py plugin with convfilt
62 autopeak [rebase] [pl=value] [t=value] [noauto] [reclick]
64 rebase : Re-asks baseline interval
66 pl=[value] : Use a fixed persistent length for the fit. If pl is not given,
67 the fit will be a 2-variable
68 fit. DO NOT put spaces between 'pl', '=' and the value.
69 The value must be in meters.
70 Scientific notation like 0.35e-9 is fine.
72 t=[value] : Use a user-defined temperature. The value must be in
73 kelvins; by default it is 293 K.
74 DO NOT put spaces between 't', '=' and the value.
76 noauto : allows for clicking the contact point by
77 hand (otherwise it is automatically estimated) the first time.
78 If subsequent measurements are made, the same contact point
79 clicked the first time is used
81 reclick : redefines by hand the contact point, if noauto has been used before
82 but the user is unsatisfied of the previously choosen contact point.
84 usepoints : fit interval by number of points instead than by nanometers
86 noflatten : does not use the "flatten" plot manipulator
88 When you first issue the command, it will ask for the filename. If you are giving the filename
89 of an existing file, autopeak will resume it and append measurements to it. If you are giving
90 a new filename, it will create the file and append to it until you close Hooke.
93 Useful variables (to set with SET command):
95 fit_function = type of function to use for elasticity. If "wlc" worm-like chain is used, if "fjc" freely jointed
98 temperature= temperature of the system for wlc/fjc fit (in K)
100 auto_slope_span = number of points on which measure the slope, for slope
102 auto_fit_nm = number of nm to fit before the peak maximum, for WLC/FJC (if usepoints false)
103 auto_fit_points = number of points to fit before the peak maximum, for WLC/FJC (if usepoints true)
105 baseline_clicks = -1: no baseline, f=0 at the contact point (whether hand-picked or automatically found)
106 0: automatic baseline
107 1: decide baseline with a single click and length defined in auto_left_baseline
108 2: let user click points of baseline
109 auto_left_baseline = length in nm to use as baseline from the right point (if baseline_clicks=0 , 1)
110 auto_right_baseline = distance in nm of peak-most baseline point from last peak (if baseline_clicks = 0)
112 auto_min_p ; auto_max_p = Minimum and maximum persistence length (if using WLC) or Kuhn length (if using FJC)
113 outside of which the peak is automatically discarded (in nm)
116 #default fit etc. variables
118 T=self.config['temperature']
120 slope_span=int(self.config['auto_slope_span'])
122 rebase=False #if true=we select rebase
123 noflatten=False #if true=we avoid flattening
125 #initialize output data vectors
134 displayed_plot=self._get_displayed_plot(0)
136 #COMMAND LINE PARSING
137 #--Using points instead of nm interval
138 if 'usepoints' in args.split():
139 fit_points=int(self.config['auto_fit_points'])
144 #--Recalculate baseline
145 if 'rebase' in args or (self.basecurrent != self.current.path):
148 if 'noflatten' in args:
151 #--Custom persistent length / custom temperature
152 for arg in args.split():
153 #look for a persistent length argument.
155 pl_expression=arg.split('=')
156 pl_value=float(pl_expression[1]) #actual value
157 #look for a T argument. FIXME: spaces are not allowed between 'pl' and value
158 if ('t=' in arg[0:2]) or ('T=' in arg[0:2]):
159 t_expression=arg.split('=')
160 T=float(t_expression[1])
161 #--Contact point arguments
162 if 'reclick' in args.split():
163 print 'Click contact point'
164 contact_point, contact_point_index = self.pickup_contact_point()
165 elif 'noauto' in args.split():
166 if self.wlccontact_index==None or self.wlccurrent != self.current.path:
167 print 'Click contact point'
168 contact_point , contact_point_index = self.pickup_contact_point()
170 contact_point=self.wlccontact_point
171 contact_point_index=self.wlccontact_index
173 #Automatically find contact point
174 cindex=self.find_contact_point()
175 contact_point=self._clickize(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1], cindex)
176 #--END COMMAND LINE PARSING--
179 peak_location, peak_size = self.find_current_peaks(noflatten)
181 if len(peak_location) == 0:
182 print 'No peaks to fit.'
185 fitplot=copy.deepcopy(displayed_plot)
187 #Pick up force baseline
189 self.basepoints=self.baseline_points(peak_location, displayed_plot)
191 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
193 to_average=displayed_plot.vectors[1][1][boundaries[0]:boundaries[1]] #y points to average
194 avg=np.mean(to_average)
196 clicks=self.config['baseline_clicks']
199 avg=displayed_plot.vectors[1][1][contact_point_index]
201 avg=displayed_plot.vectors[1][1][cindex]
203 for peak in peak_location:
207 fit_points=self.fit_interval_nm(peak, displayed_plot, self.config['auto_fit_nm'], True)
208 peak_point=self._clickize(displayed_plot.vectors[1][0],displayed_plot.vectors[1][1],peak)
209 other_fit_point=self._clickize(displayed_plot.vectors[1][0],displayed_plot.vectors[1][1],peak-fit_points)
212 points=[contact_point, peak_point, other_fit_point]
214 if abs(peak_point.index-other_fit_point.index) < 2:
217 if self.config['fit_function']=='wlc':
219 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)
220 elif self.config['fit_function']=='fjc':
221 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)
223 print 'Unknown fit function'
224 print 'Please set fit_function as wlc or fjc'
229 delta_to_measure=displayed_plot.vectors[1][1][peak-delta_force:peak+delta_force]
230 y=min(delta_to_measure)
231 #save force values (pN)
233 slope=self.linefit_between(peak-slope_span,peak)[0]
236 #check fitted data and, if right, add peak to the measurement
237 #FIXME: code duplication
238 if len(params)==1: #if we did choose 1-value fit
239 p_lengths.append(pl_value)
240 c_lengths.append(params[0]*(1.0e+9))
241 sigma_p_lengths.append(0)
242 sigma_c_lengths.append(fit_errors[0]*(1.0e+9))
243 forces.append(abs(y-avg)*(1.0e+12))
245 #Add WLC fit lines to plot
246 fitplot.add_set(xfit,yfit)
247 if len(fitplot.styles)==0:
251 fitplot.styles.append(None)
252 fitplot.colors.append(None)
254 p_leng=params[1]*(1.0e+9)
255 #check if persistent length makes sense. otherwise, discard peak.
256 if p_leng>self.config['auto_min_p'] and p_leng<self.config['auto_max_p']:
257 p_lengths.append(p_leng)
258 c_lengths.append(params[0]*(1.0e+9))
259 sigma_c_lengths.append(fit_errors[0]*(1.0e+9))
260 sigma_p_lengths.append(fit_errors[1]*(1.0e+9))
261 forces.append(abs(y-avg)*(1.0e+12))
264 #Add WLC fit lines to plot
265 fitplot.add_set(xfit,yfit)
266 if len(fitplot.styles)==0:
270 fitplot.styles.append(None)
271 fitplot.colors.append(None)
276 #add basepoints to fitplot
277 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]])
278 fitplot.styles.append('scatter')
279 fitplot.colors.append(None)
281 #Show wlc fits and peak locations
282 self._send_plot([fitplot])
284 print 'Using fit function: ',self.config['fit_function']
285 print 'Measurements for all peaks detected:'
286 print 'contour (nm)', c_lengths
287 print 'sigma contour (nm)',sigma_c_lengths
288 print 'p (nm)',p_lengths
289 print 'sigma p (nm)',sigma_p_lengths
290 print 'forces (pN)',forces
291 print 'slopes (N/m)',slopes
294 while controller==False:
295 #Ask the user what peaks to ignore from analysis.
296 print 'Peaks to ignore (0,1...n from contact point,return to take all)'
297 print 'N to discard measurement'
298 exclude_raw=raw_input('Input:')
304 if not exclude_raw=='':
305 exclude=exclude_raw.split(',')
307 exclude=[int(item) for item in exclude]
313 sigma_c_lengths[i]=None
314 sigma_p_lengths[i]=None
320 #Clean data vectors from ignored peaks
321 #FIXME:code duplication
322 c_lengths=[item for item in c_lengths if item != None]
323 p_lengths=[item for item in p_lengths if item != None]
324 forces=[item for item in forces if item != None]
325 slopes=[item for item in slopes if item != None]
326 sigma_c_lengths=[item for item in sigma_c_lengths if item != None]
327 sigma_p_lengths=[item for item in sigma_p_lengths if item != None]
329 print 'Measurements for chosen peaks:'
330 print 'contour (nm)',c_lengths
331 print 'sigma contour (nm)',sigma_c_lengths
332 print 'p (nm)',p_lengths
333 print 'sigma p (nm)',sigma_p_lengths
334 print 'forces (pN)',forces
335 print 'slopes (N/m)',slopes
338 if self.autofile=='':
339 self.autofile=raw_input('Autopeak filename? (return to ignore) ')
340 if self.autofile=='':
344 if not os.path.exists(self.autofile):
345 f=open(self.autofile,'w+')
346 f.write('Analysis started '+time.asctime()+'\n')
347 f.write('----------------------------------------\n')
348 f.write('; Contour length (nm) ; Persistence length (nm) ; Max.Force (pN) ; Slope (N/m) ; Sigma contour (nm) ; Sigma persistence (nm)\n')
352 f=open(self.autofile,'a+')
354 f.write(self.current.path+'\n')
355 for i in range(len(c_lengths)):
356 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')
359 self.do_note('autopeak')