6 Automatic peak detection and analysis.
9 with modifications by Dr. Rolf Schmidt (Concordia University, Canada)
11 This program is released under the GNU General Public License version 2.
14 import lib.libhooke as lh
16 wxversion.select(lh.WX_GOOD)
19 from numpy import mean, RankWarning
22 warnings.simplefilter('ignore', RankWarning)
28 class autopeakCommands:
30 Autopeak carries out force curve fitting with a chosen model:
36 def do_autopeak(self, plot=None):
40 Automatically performs a number of analyses on the peaks of the given curve.
41 Currently it automatically:
42 - fits peaks with WLC or FJC function (depending on how the fit_function variable is set)
43 - measures peak maximum forces with a baseline
44 - measures slope in proximity of peak maximum
45 Requires flatten plotmanipulator , fit.py plugin , flatfilts.py plugin with convfilt
48 autopeak [rebase] [persistence_length=value] [t=value] [noauto] [reclick]
50 rebase : Re-asks baseline interval
52 persistence_length=[value] : Use a fixed persistent length for the fit. If persistence_length is not given,
53 the fit will be a 2-variable
54 fit. DO NOT put spaces between 'persistence_length', '=' and the value.
55 The value must be in nanometers.
56 Scientific notation like 0.35 is fine.
58 t=[value] : Use a user-defined temperature. The value must be in
59 kelvins; by default it is 293 K.
60 DO NOT put spaces between 't', '=' and the value.
62 noauto : allows for clicking the contact point by
63 hand (otherwise it is automatically estimated) the first time.
64 If subsequent measurements are made, the same contact point
65 clicked the first time is used
67 reclick : redefines by hand the contact point, if noauto has been used before
68 but the user is unsatisfied of the previously choosen contact point.
70 usepoints : fit interval by number of points instead than by nanometers
72 noflatten : does not use the "flatten" plot manipulator
74 When you first issue the command, it will ask for the filename. If you are giving the filename
75 of an existing file, autopeak will resume it and append measurements to it. If you are giving
76 a new filename, it will create the file and append to it until you close Hooke.
79 Useful variables (to set with SET command):
81 fit_function = type of function to use for elasticity. If "wlc" worm-like chain is used, if "fjc" freely jointed
84 temperature= temperature of the system for wlc/fjc fit (in K)
86 auto_slope_span = number of points on which measure the slope, for slope
88 auto_fit_nm = number of nm to fit before the peak maximum, for WLC/FJC (if usepoints false)
89 auto_fit_points = number of points to fit before the peak maximum, for WLC/FJC (if usepoints true)
91 baseline_clicks = contact point: no baseline, f=0 at the contact point (whether hand-picked or automatically found)
92 automatic: automatic baseline
93 1 point: decide baseline with a single click and length defined in auto_left_baseline
94 2 points: let user click points of baseline
95 auto_left_baseline = length in nm to use as baseline from the right point (if baseline_clicks = automatic , 1 point)
96 auto_right_baseline = distance in nm of peak-most baseline point from last peak (if baseline_clicks = automatic)
98 auto_min_p ; auto_max_p = Minimum and maximum persistence length (if using WLC) or Kuhn length (if using FJC)
99 outside of which the peak is automatically discarded (in nm)
102 #default fit etc. variables
103 auto_fit_nm = self.GetFloatFromConfig('autopeak', 'auto_fit_nm')
104 auto_left_baseline = self.GetFloatFromConfig('autopeak', 'auto_left_baseline')
105 auto_max_p = self.GetFloatFromConfig('autopeak', 'auto_max_p')
106 auto_min_p = self.GetFloatFromConfig('autopeak', 'auto_min_p')
107 auto_right_baseline = self.GetFloatFromConfig('autopeak', 'auto_right_baseline')
108 baseline_clicks = self.GetStringFromConfig('autopeak', 'baseline_clicks')
109 fit_function = self.GetStringFromConfig('autopeak', 'fit_function')
110 fit_points = self.GetIntFromConfig('autopeak', 'auto_fit_points')
111 noauto = self.GetBoolFromConfig('autopeak', 'noauto')
112 #noflatten: if true we do not flatten the curve
113 noflatten = self.GetBoolFromConfig('autopeak', 'noflatten')
114 peak_show = self.GetBoolFromConfig('autopeak', 'peak_show')
115 persistence_length = self.GetFloatFromConfig('autopeak', 'persistence_length')
116 #rebase: redefine the baseline
117 rebase = self.GetBoolFromConfig('autopeak', 'rebase')
118 reclick = self.GetBoolFromConfig('autopeak', 'reclick')
119 slope_span = self.GetIntFromConfig('autopeak', 'auto_slope_span')
120 T = self.GetFloatFromConfig('autopeak', 'temperature')
121 usepl = self.GetBoolFromConfig('autopeak', 'usepl')
125 pl_value = persistence_length / 10**9
126 usepoints = self.GetBoolFromConfig('autopeak', 'usepoints')
127 whatset_str = self.GetStringFromConfig('autopeak', 'whatset')
128 if whatset_str == 'extension':
129 whatset = lh.EXTENSION
130 if whatset_str == 'retraction':
131 whatset = lh.RETRACTION
133 #TODO: should this be variable?
136 #setup header column labels for results
137 if fit_function == 'wlc':
138 fit_results = lib.results.ResultsWLC()
139 segment_str = 'Persistence length'
140 sigma_segment_str = 'sigma persistence length'
141 elif fit_function == 'fjc' or fit_function == 'fjcPEG':
142 fit_results = lib.results.ResultsFJC()
143 segment_str = 'Kuhn length'
144 sigma_segment_str = 'sigma Kuhn length'
146 self.AppendToOutput('Unknown fit function, Please set fit_function as wlc, fjc or fjcPEG')
149 #initialize output data vectors
159 plot = copy.deepcopy(self.GetActivePlot())
160 filename = self.GetActiveFile().name
162 #apply all active plotmanipulators and add the 'manipulated' data
163 for plotmanipulator in self.plotmanipulators:
164 if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name):
165 if plotmanipulator.name == 'flatten':
167 plot = plotmanipulator.method(plot, self.GetActiveFile())
169 plot = plotmanipulator.method(plot, self.GetActiveFile())
171 #--Using points instead of nm interval
175 #--Contact point arguments
177 contact_point, contact_point_index = self.pickup_contact_point(filename=filename)
179 if self.wlccontact_index is None or self.wlccurrent != filename:
180 contact_point, contact_point_index = self.pickup_contact_point(filename=filename)
182 contact_point = self.wlccontact_point
183 contact_point_index = self.wlccontact_index
185 #Automatically find contact point
186 cindex = self.find_contact_point(plot)
187 contact_point = self._clickize(plot.curves[lh.RETRACTION].x, plot.curves[lh.EXTENSION].y, cindex)
189 #peak_size comes from convolution curve
190 peak_location, peak_size = self.find_current_peaks(plot=plot, noflatten=noflatten)
192 if len(peak_location) == 0:
193 self.AppendToOutput('No peaks to fit.')
196 #Pick up force baseline
197 if baseline_clicks == 'contact point':
199 avg = plot.curves[lh.RETRACTION].y[contact_point_index]
201 avg = plot.curves[lh.RETRACTION].y[cindex]
203 if rebase or (self.basecurrent != filename) or self.basepoints is None:
204 if baseline_clicks == 'automatic':
206 base_index_0 = peak_location[-1] + self.fit_interval_nm(peak_location[-1], plot.curves[lh.RETRACTION].x, auto_right_baseline, False)
207 self.basepoints.append(self._clickize(plot.curves[lh.RETRACTION].x, plot.curves[lh.RETRACTION].y, base_index_0))
208 base_index_1 = self.basepoints[0].index + self.fit_interval_nm(self.basepoints[0].index, plot.curves[lh.RETRACTION].x, auto_left_baseline, False)
209 self.basepoints.append(self._clickize(plot.curves[lh.RETRACTION].x, plot.curves[lh.RETRACTION].y, base_index_1))
210 if baseline_clicks == '1 point':
211 self.basepoints=self._measure_N_points(N=1, message='Click on 1 point to select the baseline.', whatset=whatset)
212 base_index_1 = self.basepoints[0].index + self.fit_interval_nm(self.basepoints[0].index, plot.curves[lh.RETRACTION].x, auto_left_baseline, False)
213 self.basepoints.append(self._clickize(plot.curves[lh.RETRACTION].x, plot.curves[lh.RETRACTION].y, base_index_1))
214 if baseline_clicks == '2 points':
215 self.basepoints=self._measure_N_points(N=2, message='Click on 2 points to select the baseline.', whatset=whatset)
216 if baseline_clicks != 'contact point':
217 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
219 to_average = plot.curves[lh.RETRACTION].y[boundaries[0]:boundaries[1]] #y points to average
220 avg = mean(to_average)
221 self.basecurrent = filename
223 x_values = plot.curves[lh.RETRACTION].x
224 y_values = plot.curves[lh.RETRACTION].y
225 for index, peak in enumerate(peak_location):
229 fit_points = self.fit_interval_nm(peak, plot.curves[lh.RETRACTION].x, auto_fit_nm, True)
230 peak_point = self._clickize(x_values, y_values, peak)
231 other_fit_point=self._clickize(x_values, y_values, peak - fit_points)
234 points = [contact_point, peak_point, other_fit_point]
236 if abs(peak_point.index - other_fit_point.index) < 2:
239 if fit_function == 'wlc':
240 params, yfit, xfit, fit_errors = self.wlc_fit(points, x_values, y_values, pl_value, T, return_errors=True)
241 elif fit_function == 'fjc':
242 params, yfit, xfit, fit_errors = self.fjc_fit(points, x_values, y_values, pl_value, T, return_errors=True)
243 elif fit_function == 'fjcPEG':
244 params, yfit, xfit, fit_errors = self.fjcPEG_fit(points, x_values, y_values, pl_value, T, return_errors=True)
247 delta_to_measure = y_values[peak - delta_force:peak + delta_force]
248 y = min(delta_to_measure)
249 #save force values (pN)
251 slope = self.linefit_between(peak - slope_span, peak, whatset=lh.RETRACTION)[0]
253 #check fitted data and, if right, add peak to the measurement
254 fit_result = lib.results.Result()
256 fit_result.result['Contour length'] = params[0]
257 fit_result.result['sigma contour length'] = fit_errors[0]
258 fit_result.result['Rupture force'] = abs(y - avg)
259 fit_result.result['Slope'] = slope
260 active_file = self.GetActiveFile()
261 if active_file.driver.retract_velocity:
262 fit_result.result['Loading rate'] = slope * active_file.driver.retract_velocity
264 fit_result.result['Loading rate'] = -1
265 if len(params) == 1: #if we did choose 1-value fit
266 fit_result.result[segment_str] = pl_value
267 fit_result.result[sigma_segment_str] = 0
269 p_lengths.append(pl_value)
270 c_lengths.append(params[0]*(1.0e+9))
271 sigma_p_lengths.append(0)
272 sigma_c_lengths.append(fit_errors[0]*(1.0e+9))
273 forces.append(abs(y-avg)*(1.0e+12))
276 p_leng = params[1] * (1.0e+9)
277 #check if persistence length makes sense, otherwise discard peak.
278 if p_leng > auto_min_p and p_leng < auto_max_p:
279 fit_result.result[segment_str] = params[1]
280 fit_result.result[sigma_segment_str] = fit_errors[1]
282 p_lengths.append(p_leng)
283 c_lengths.append(params[0]*(1.0e+9))
284 sigma_c_lengths.append(fit_errors[0]*(1.0e+9))
285 sigma_p_lengths.append(fit_errors[1]*(1.0e+9))
286 forces.append(abs(y-avg)*(1.0e+12))
289 fit_result.result = {}
291 if len(fit_result.result) > 0:
292 fit_result.label = fit_function + '_' + str(index)
293 fit_result.title = plot.curves[lh.RETRACTION].title
294 fit_result.units.x = plot.curves[lh.RETRACTION].units.x
295 fit_result.units.y = plot.curves[lh.RETRACTION].units.y
296 fit_result.visible = True
299 fit_results.results.append(fit_result)
301 if fit_results.results:
302 fit_results.set_multipliers(0)
303 plot = self.GetActivePlot()
304 plot.results[fit_function] = fit_results
306 plugin = lib.plugin.Plugin()
307 plugin.name = 'autopeak'
308 plugin.section = 'autopeak'
309 plugin.prefix = 'peak_'
310 self.do_peaks(plugin=plugin, peak_location=peak_location, peak_size=peak_size)
315 self.AppendToOutput('No peaks found.')
318 #self.do_note('autopeak')
320 def find_current_peaks(self, plot=None, noflatten=True):
322 plot_temp = self.plotmanip_flatten(plot, self.GetActiveFile(), customvalue=1)
323 peak_location, peak_size = self.has_peaks(plot_temp)
324 return peak_location, peak_size
326 def fit_interval_nm(self, start_index, x_vect, nm, backwards):
328 Calculates the number of points to fit, given a fit interval in nm
329 start_index: index of point
331 backwards: if true, finds a point backwards.
337 while abs(x_vect[i] - x_vect[start_index]) * (10**9) < nm:
338 if i == 0 or i == maxlen-1: #we reached boundaries of vector!
347 def pickup_contact_point(self, filename=''):
349 macro to pick up the contact point by clicking
351 contact_point = self._measure_N_points(N=1, message='Please click on the contact point.')[0]
352 contact_point_index = contact_point.index
353 self.wlccontact_point = contact_point
354 self.wlccontact_index = contact_point.index
355 self.wlccurrent = filename
356 return contact_point, contact_point_index