Add TranslateFlatPeaksCommand.
authorW. Trevor King <wking@drexel.edu>
Tue, 10 Aug 2010 20:32:31 +0000 (16:32 -0400)
committerW. Trevor King <wking@drexel.edu>
Tue, 10 Aug 2010 20:32:31 +0000 (16:32 -0400)
This completes a simple velocity clamp force analysis approach.  Consider:

  ./bin/hooke -c 'load_playlist test/data/test'
              -c 'zero_block_surface_contact_point --block 1'
              -c 'add_flattened_extension_array --block 1 --max_degree 1
                      --input_deflection_column "surface deflection (m)"'
              -c 'add_block_force_array --block 1
                      --input_deflection_column "flattened deflection (m)"'
              -c 'add_block_cantilever_adjusted_extension_array --block 1'
              -c 'flat_filter_peaks --min_points 1'
              -c 'flat_peaks_to_polymer_peaks --block 1'
              -c 'polymer_fit_peaks --block 1'
              --command-no-exit

Still to come:
* Better contact point fitting or flattening, since the above method
  currently does a pretty bad job with the actual WLCs.  Perhaps a
  WLC-based zeroing could lead a second fitting iteration.
* A way to apply grouped commands like this to each curve in a playlist.
* Saving dicts in the playlist format or changing formats so the WLC
  fitting parameters are accessible for further analysis (e.g. with
  sawsim: http://dx.doi.org/10.1016/j.ijbiomac.2009.12.001 ).

hooke/plugin/flatfilt.py
hooke/plugin/polymer_fit.py

index a1857fbfd66f648a6ac370c0102fd98149ac11b8..ed08a3492b7fd1713382f23bf78068e53afac428 100644 (file)
@@ -108,7 +108,7 @@ Name of the column (without units) to use as the peak-maske deflection output.
                 Argument(name='peak info name', type='string',
                          default='flat filter peaks',
                          help="""
-Name for storing the distance offset in the `.info` dictionary.
+Name for storing the list of peaks in the `.info` dictionary.
 """.strip()),
                 ])
         self._commands = [FlatPeaksCommand(self), FlatFilterCommand(self)]
index 43ed44ba00d5eb8cb3bff8d64d12a3143d9323e3..88058a629a3d6f2c6d3f2e710629927c35642605 100644 (file)
@@ -40,6 +40,7 @@ from ..curve import Data
 from ..plugin import Plugin, argument_to_setting
 from ..util.callback import is_iterable
 from ..util.fit import PoorFit, ModelFitter
+from ..util.peak import Peak
 from ..util.si import join_data_label, split_data_label
 from .curve import CurveArgument
 from .vclamp import scale
@@ -920,6 +921,7 @@ Name of the column to use as the deflection input.
                 ])
         self._commands = [
             PolymerFitCommand(self), PolymerFitPeaksCommand(self),
+            TranslateFlatPeaksCommand(self),
             ]
 
     def dependencies(self):
@@ -1110,9 +1112,9 @@ approach/retract force curve, `0` selects the approaching curve and
 `1` selects the retracting curve.
 """.strip()),
                 Argument(name='peak info name', type='string',
-                         default='flat filter peaks',
+                         default='polymer peaks',
                          help="""
-Name for storing the distance offset in the `.info` dictionary.
+Name for the list of peaks in the `.info` dictionary.
 """.strip()),
                 Argument(name='peak index', type='int', count=-1, default=None,
                          help="""
@@ -1141,6 +1143,80 @@ Index of the selected peak in the list of peaks.  Use `None` to fit all peaks.
             if not isinstance(ret, Success):
                 raise ret
 
+
+class TranslateFlatPeaksCommand (Command):
+    """Translate flat filter peaks into polymer peaks for fitting.
+
+    Use :class:`~hooke.plugin.flatfilt.FlatPeaksCommand` creates a
+    list of peaks for regions with large derivatives.  For velocity
+    clamp measurements, these regions are usually the rebound phase
+    after a protein domain unfolds, the cantilever detaches, etc.
+    Because these features occur after the polymer loading phase, we
+    need to shift the selected regions back to align them with the
+    polymer loading regions.
+    """
+    def __init__(self, plugin):
+        plugin_arguments = [a for a in plugin._arguments
+                            if a.name in ['input distance column',
+                                          'input deflection column']]
+        arguments = [
+            CurveArgument,
+            Argument(name='block', aliases=['set'], type='int', default=0,
+                     help="""
+Data block for which the fit should be calculated.  For an
+approach/retract force curve, `0` selects the approaching curve and
+`1` selects the retracting curve.
+""".strip()),
+            ] + plugin_arguments + [
+            Argument(name='input peak info name', type='string',
+                     default='flat filter peaks',
+                     help="""
+Name for the input peaks in the `.info` dictionary.
+""".strip()),
+            Argument(name='output peak info name', type='string',
+                     default='polymer peaks',
+                     help="""
+Name for the ouptput peaks in the `.info` dictionary.
+""".strip()),
+            Argument(name='end offset', type='int', default=-1,
+                     help="""
+Number of points between the end of a new peak and the start of the old.
+""".strip()),
+            Argument(name='start fraction', type='float', default=0.2,
+                     help="""
+Place the start of the new peak at `start_fraction` from the end of
+the previous old peak to the end of the new peak.  Because the first
+new peak will have no previous old peak, it uses a distance of zero
+instead.
+""".strip()),
+            ]
+        super(TranslateFlatPeaksCommand, self).__init__(
+            name='flat peaks to polymer peaks',
+            arguments=arguments,
+            help=self.__doc__, plugin=plugin)
+
+    def _run(self, hooke, inqueue, outqueue, params):
+        data = params['curve'].data[params['block']]
+        z_data = data[:,data.info['columns'].index(
+                params['input distance column'])]
+        d_data = data[:,data.info['columns'].index(
+                params['input deflection column'])]
+        previous_old_stop = numpy.absolute(z_data).argmin()
+        new = []
+        for i,peak in enumerate(data.info[params['input peak info name']]):
+            next_old_start = peak.index
+            stop = next_old_start + params['end offset'] 
+            z_start = z_data[previous_old_stop] + params['start fraction']*(
+                z_data[stop] - z_data[previous_old_stop])
+            start = numpy.absolute(z_data - z_start).argmin()
+            p = Peak('polymer peak %d' % i,
+                     index=start,
+                     values=d_data[start:stop])
+            new.append(p)
+            previous_old_stop = peak.post_index()
+        data.info[params['output peak info name']] = new
+
+
 # TODO:
 # def dist2fit(self):
 #     '''Calculates the average distance from data to fit, scaled by