Don't specify curve or data block types. See doc/standards.txt.
authorW. Trevor King <wking@drexel.edu>
Tue, 7 Sep 2010 18:43:11 +0000 (14:43 -0400)
committerW. Trevor King <wking@drexel.edu>
Tue, 7 Sep 2010 18:43:11 +0000 (14:43 -0400)
20 files changed:
doc/analysis.txt [new file with mode: 0644]
doc/index.txt
doc/standards.txt [new file with mode: 0644]
hooke/curve.py
hooke/driver/__init__.py
hooke/driver/csvdriver.py
hooke/driver/hdf5.py
hooke/driver/hemingway.py
hooke/driver/jpk.py
hooke/driver/mcs.py
hooke/driver/mfp1d.py
hooke/driver/mfp1dexport.py
hooke/driver/mfp3d.py
hooke/driver/picoforce.py
hooke/driver/tutorial.py
hooke/driver/wtk.py
hooke/experiment.py [deleted file]
hooke/plugin/convfilt.py
hooke/plugin/curve.py
hooke/plugin/flatfilt.py

diff --git a/doc/analysis.txt b/doc/analysis.txt
new file mode 100644 (file)
index 0000000..1286393
--- /dev/null
@@ -0,0 +1,85 @@
+******************
+Analysis templates
+******************
+
+.. toctree::
+   :maxdepth: 2
+
+Overview
+========
+
+Hooke is a large, flexible beast, and the number of available commands
+can be intimidating.  In this section, we'll lay out the procedure for
+analyzing a number of common experiment types.  The experiment types
+are arranged in the order that they were developed, citating the first
+known paper in each class.  If we've got a citation wrong, please
+submit a bug report (:doc:`bugs`).
+
+
+Velocity clamp force spectroscopy
+=================================
+
+Developed by Rief et al. [#rief1997]_, velocity clamp spectroscopy
+measures domain unfolding forces as the tip is moved away from the
+surface at a constant velocity.
+
+.. todo:: Velocity clamp example plot.
+
+Basic analysis is something like::
+
+    hooke> zero_surface_contact_point --block retract
+    hooke> flat_filter_peaks --block retract --min_points 1
+    hooke> zero_surface_contact_point --block retract
+    ...        --ignore_after_last_peak_info_name 'flat filter peaks'
+    hooke> convert_distance_to_force --block retract
+    ...        --deflection_column 'surface deflection (m)'
+    hooke> remove_cantilever_from_extension --block retract
+    hooke> flat_peaks_to_polymer_peaks --block retract
+    hooke> polymer_fit_peaks --block retract
+    hooke> block_info --block retract --output data.yaml name 'polymer peak [0-9]*'
+
+The unfolding forces should be grouped by pull velocity, and the resulting
+distributions can be fit to specific unfolding models by comparison with
+monte-carlo simulations (see, for example, sawsim_).
+
+.. [#rief1997] M. Rief, M. Gautel, F. Oesterhelt, J.M. Fernandez,
+  H.E. Gaub.
+  "Reversible Unfolding of Individual Titin Immunoglobulin Domains by AFM."
+  Science, 1997.
+  doi: `10.1126/science.276.5315.1109 <http://dx.doi.org/10.1126/science.276.5315.1109>`_
+
+.. _sawsim: http://www.physics.drexel.edu/~wking/sawsim/
+
+Force clamp spectroscopy
+========================
+
+Developed by Fernandez and Li [#fernandez2004]_, force clamp spectroscopy
+measures the lifetime of a domain's folded state while subject to a constant
+tension.
+
+.. todo:: Force clamp example plot.
+
+.. todo:: Force clamp example analysis.
+
+.. [#fernandez2004] J.M. Fernandez, H. Li.
+  "Force-Clamp Spectroscopy Monitors the Folding Trajectory of a
+  Single Protein."
+  Science, 2004.
+  doi: `10.1126/science.1092497 <http://dx.doi.org/10.1126/science.1092497>`_
+
+Two-color fluorescence coincidence spectroscopy
+===============================================
+
+Developed by Clarke et al. [#clarke2007]_,...
+
+.. todo:: TCCS explanation.
+
+.. todo:: TCCS example plot.
+
+.. todo:: TCCS example analysis.
+
+.. [#clarke2007] R.W. Clarke, A. Orte, D. Klenerman.
+  "Optimized Threshold Selection for Single-Molecule Two-Color
+  Fluorescence Coincidence Spectroscopy."
+  Anal. Chem., 2007.
+  doi: `10.1021/ac062188w <http://dx.doi.org/10.1021/ac062188w>`_
index 37a1167..a067fb7 100644 (file)
@@ -28,9 +28,11 @@ Contents
 
    install
    tutorial
+   analysis
    config
    bugs
    hacking
+   standards
    hooke/hooke
    doc
 
diff --git a/doc/standards.txt b/doc/standards.txt
new file mode 100644 (file)
index 0000000..743b9aa
--- /dev/null
@@ -0,0 +1,63 @@
+********************
+Standard experiments
+********************
+
+.. toctree::
+   :maxdepth: 2
+
+Overview
+========
+
+Force spectroscopy experiments are generally classified as "force
+clamp", "velocity clamp", etc..  While previous versions of Hooke
+attempted to reproduce such classification, I now believe this
+classification is counter-productive.  The type of experiment carried
+out by a given :class:`~hooke.curve.Curve` is determined by the
+sequence of :class:`~hooke.curve.Data` blocks in the curve.  For
+example, a classic velocity clamp experiment would look like::
+
+    hooke> curve_info --enable-block_names
+    block names: ['approach', 'pause', 'retract']
+
+And classic force clamp experiment would look like::
+
+    hooke> curve_info --enable-block_names
+    block names: ['approach', 'pause', 'retract', 'force clamp']
+
+However, the situation is complicated by refolding experiments such as::
+
+    hooke> curve_info --enable-block_names
+    block names: ['approach-0', 'pause-0', 'retract-0',
+                  'approach-1', 'pause-1', 'retract-1',
+                  'approach-2', 'pause-2', 'retract-2',
+                 ...]
+
+The benefit to classifying curves in Hooke is that each class of
+experiment has a standard analysis procedure (e.g. unfolding force
+extraction for velocity clamp experiments or survival time extraction
+for force clamp experiments).  However, the potential convenience of
+Hooke being able to guess a user's desired analysis is outweighed by
+the increased complexity and rigidity that such guessing requires.
+Instead we provide :class:`~hooke.command.Command`\s that can be
+applied to any data block, and leave it up to the user to construct a
+sequence of analysis commands that makes sense.  The risk to this
+flexible approach is confusion for both the user and the Hooke
+developer.  The remainder of this section lays out a scheme for
+maintaining developer sanity.  Users should read over the
+:doc:`analysis` section for an overview of analysis procedures
+that are standardized enough to have written documentation.
+
+Drivers
+=======
+
+.. todo:: Explain driver responsibilities.
+
+Commands
+========
+
+.. todo:: Explain command analysis and storage behaviour.
+
+Results
+=======
+
+.. todo:: Explain procedure for exporting analysis results.
index fc8701b..030efcb 100644 (file)
@@ -28,7 +28,6 @@ import os.path
 import numpy
 
 from .command_stack import CommandStack
-from . import experiment
 
 
 class NotRecognized (ValueError):
@@ -156,23 +155,14 @@ class Curve (object):
     would consist of the approach data and the retract data.  Metadata
     would be the temperature, cantilever spring constant, etc.
 
-    Two important :attr:`info` settings are `filetype` and
-    `experiment`.  These are two strings that can be used by Hooke
-    commands/plugins to understand what they are looking at.
-
-    * :attr:`info['filetype']` should contain the name of the exact
-      filetype defined by the driver (so that filetype-speciofic
-      commands can know if they're dealing with the correct filetype).
-    * :attr:`info['experiment']` should contain an instance of a
-      :class:`hooke.experiment.Experiment` subclass to identify the
-      experiment type.  For example, various
-      :class:`hooke.driver.Driver`\s can read in force-clamp data, but
-      Hooke commands could like to know if they're looking at force
-      clamp data, regardless of their origin.
-
-    Another important attribute is :attr:`command_stack`, which holds
-    a :class:`~hooke.command_stack.CommandStack` listing the commands
-    that have been applied to the `Curve` since loading.
+    Each :class:`Data` block in :attr:`data` must contain an
+    :attr:`info['name']` setting with a unique (for the parent
+    curve) name identifying the data block.  This allows plugins 
+    and commands to access individual blocks.
+
+    Each curve maintiains a :class:`~hooke.command_stack.CommandStack`
+    (:attr:`command_stack`) listing the commands that have been
+    applied to the `Curve` since loading.
 
     The data-type is pickleable, to ensure we can move it between
     processes with :class:`multiprocessing.Queue`\s.
index bc38a24..8704f5d 100644 (file)
@@ -89,9 +89,7 @@ class Driver (object):
         The input `info` :class:`dict` may contain attributes read
         from the :class:`~hooke.playlist.FilePlaylist`.
 
-        The `info` :class:`dict` must contain values for the keys:
-        'filetype' and 'experiment'.  See :class:`hooke.curve.Curve`
-        for details.
+        See :class:`hooke.curve.Curve` for details.
         """
         raise NotImplementedError
 
index 3ec4cbe..5713233 100644 (file)
@@ -41,10 +41,6 @@ class csvdriverDriver(lib.driver.Driver):
         self.filedata = open(filename,'r')
         self.data = list(self.filedata)
         self.filedata.close()
-
-        self.filetype = 'generic'
-        self.experiment = ''
-
         self.filename=filename
 
     def close_all(self):
index f338324..64f09be 100644 (file)
@@ -35,9 +35,6 @@ class hdf5Driver(lhc.Driver):
         self.lines=list(self.filedata.readlines())
         self.filedata.close()
         
-        self.filetype='hdf5'
-        self.experiment='smfs'
-        
     def close_all(self):
         self.filedata.close()
         
index af351f7..fe0fb98 100644 (file)
@@ -24,7 +24,6 @@ import os.path
 import numpy
 
 from .. import curve as curve
-from .. import experiment as experiment
 from ..util.util import Closing as Closing
 from . import Driver as Driver
 
@@ -72,7 +71,5 @@ class HemingwayDriver (Driver):
         ret.info['name'] = 'force clamp'
         # assume 1 ms timestep
         ret[:,0] = numpy.arange(0, 1e-3*data.shape[0], 1e-3, dtype=ret.dtype)
-        
-        file_info['filetype'] = self.name
-        file_info['experiment'] = experiment.ForceClamp()
+
         return ([ret,], file_info)
index d70fa3c..8ca521d 100644 (file)
@@ -30,7 +30,6 @@ import zipfile
 import numpy
 
 from .. import curve as curve
-from .. import experiment as experiment
 from ..util.util import Closing as Closing
 from ..util.si import join_data_label, split_data_label
 from . import Driver as Driver
@@ -148,16 +147,8 @@ class JPKDriver (Driver):
     def _zip_translate_params(self, params, chan_info):
         info = {
             'raw info':params,
-            'filetype':self.name,
             #'time':self._time_from_TODO(raw_info[]),
             }
-        # TODO: distinguish between force clamp and velocity clamp
-        # experiments.  Note that the JPK file format is flexible
-        # enough to support mixed experiments (i.e. both force clamp
-        # and velocity clamp segments in a single experiment), but I
-        # have no idea what sort of analysis such experiments would
-        # require ;).
-        info['experiment'] = experiment.VelocityClamp()
         force_unit = chan_info['channel']['vDeflection']['conversion-set']['conversion']['force']['scaling']['unit']['unit']
         assert force_unit == 'N', force_unit
         force_base = chan_info['channel']['vDeflection']['conversion-set']['conversion']['force']['base-calibration-slot']
index 50a5029..5e80c51 100644 (file)
@@ -53,9 +53,6 @@ class mcsDriver(lib.driver.Driver):
         self.bluedata=self.filebluedata.read()
         self.filebluedata.close()
 
-        self.filetype = 'mcs'
-        self.experiment = 'smfluo'
-
     def close_all(self):
         self.filedata.close()
         self.filebluedata.close()
index 2c314bd..355ba44 100644 (file)
@@ -58,9 +58,6 @@ class mfp1dDriver(lib.driver.Driver):
         self.lines = list(self.filedata.readlines())
         self.filedata.close()
 
-        self.filetype = 'mfp1d'
-        self.experiment = 'smfs'
-
     def _load_from_file(self, filename, extract_note=False):
         data = None
         f = open(filename, 'rb')
index 174696b..63f5851 100644 (file)
@@ -42,9 +42,6 @@ class mfp1dexportDriver(lhc.Driver):
         self.lines = list(self.filedata.readlines())
         self.filedata.close()
 
-        self.filetype='mfp1dexport'
-        self.experiment='smfs'
-
     def close_all(self):
         self.filedata.close()
 
index 3347e3d..cf71874 100644 (file)
@@ -37,7 +37,6 @@ import pprint
 import numpy
 
 from .. import curve as curve
-from .. import experiment as experiment
 from . import Driver as Driver
 from .igorbinarywave import loadibw
 
@@ -72,8 +71,6 @@ class MFP3DDriver (Driver):
     def read(self, path, info=None):
         data,bin_info,wave_info = loadibw(path)
         blocks,info = self._translate_ibw(data, bin_info, wave_info)
-        info['filetype'] = self.name
-        info['experiment'] = experiment.VelocityClamp()
         return (blocks, info)
      
     def _translate_ibw(self, data, bin_info, wave_info):
index ca84792..625c455 100644 (file)
@@ -29,7 +29,6 @@ import time
 import numpy
 
 from .. import curve as curve # this module defines data containers.
-from .. import experiment as experiment # this module defines expt. types
 from . import Driver as Driver # this is the Driver base class
 
 
@@ -54,8 +53,6 @@ class PicoForceDriver (Driver):
         info = self._read_header_path(path)
         self._check_version(info)
         data = self._read_data_path(path, info)
-        info['filetype'] = self.name
-        info['experiment'] = experiment.VelocityClamp()
         return (data, info)
 
     def _read_header_path(self, path):
index 6856f8d..abd2dae 100644 (file)
@@ -66,7 +66,6 @@ import os.path
 # The following are relative imports.  See PEP 328 for details
 #   http://www.python.org/dev/peps/pep-0328/
 from .. import curve as curve # this module defines data containers.
-from .. import experiment as experiment # this module defines expt. types
 from ..config import Setting # configurable setting class
 from . import Driver as Driver # this is the Driver base class
 
@@ -136,5 +135,5 @@ class TutorialDriver (Driver):
         f.close() # remember to close the file
 
         data = curve.Data()
-        info = {'filetype':'tutorial', 'experiment':experiment.Experiment()}
+        info = {}
         return (data, info)
index 4124701..031fb80 100644 (file)
@@ -42,7 +42,6 @@ import time
 import numpy
 
 from .. import curve as curve
-from .. import experiment as experiment
 from ..config import Setting
 from . import Driver as Driver
 
@@ -98,7 +97,7 @@ class WTKDriver (Driver):
             unlabeled_approach_data, params, 'approach')
         retract = self._scale_block(
             unlabeled_retract_data, params, 'retract')
-        info = {'filetype':self.name, 'experiment':experiment.VelocityClamp()}
+        info = {}
         return ([approach, retract], info)
 
     def _paths(self, path):
diff --git a/hooke/experiment.py b/hooke/experiment.py
deleted file mode 100644 (file)
index 646b415..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
-#
-# This file is part of Hooke.
-#
-# Hooke is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Hooke is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
-# Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with Hooke.  If not, see
-# <http://www.gnu.org/licenses/>.
-
-"""Define :class:`Experiment` and assorted subclasses.
-
-This allows :class:`hooke.plugin.Plugin`\s to specify the types of
-experiments they can handle.
-"""
-
-from .util.singleton import Singleton
-
-
-class Experiment (Singleton):
-    """Base class for experiment classification.
-    """
-    pass
-
-class ForceClamp (Experiment):
-    """Constant force force spectroscopy [#fernandez2004]_.
-
-    .. [#fernandez2004] J.M. Fernandez, H. Li.
-      "Force-Clamp Spectroscopy Monitors the Folding Trajectory of a
-      Single Protein."
-      Science, 2004.
-      doi: `10.1126/science.1092497 <http://dx.doi.org/10.1126/science.1092497>`_
-    """
-    pass
-
-class VelocityClamp (Experiment):
-    """Constant piezo velocity force spectroscopy [#rief1997]_.
-    
-    .. [#rief1997] M. Rief, M. Gautel, F. Oesterhelt, J.M. Fernandez,
-      H.E. Gaub.
-      "Reversible Unfolding of Individual Titin Immunoglobulin Domains by AFM."
-      Science, 1997.
-      doi: `10.1126/science.276.5315.1109 <http://dx.doi.org/10.1126/science.276.5315.1109>`_
-    """
-    pass
-
-class TwoColorCoincidenceDetection (Experiment):
-    """Two-color fluorescence coincidence spectroscopy [#clarke2007]_.
-
-    .. [#clarke2007] R.W. Clarke, A. Orte, D. Klenerman.
-      "Optimized Threshold Selection for Single-Molecule Two-Color
-      Fluorescence Coincidence Spectroscopy."
-      Anal. Chem., 2007.
-      doi: `10.1021/ac062188w <http://dx.doi.org/10.1021/ac062188w>`_
-    """
-    pass
index a5780e9..a741f69 100644 (file)
@@ -40,7 +40,6 @@ import numpy
 
 from ..command import Command, Argument, Success, Failure
 from ..config import Setting
-from ..experiment import VelocityClamp
 from ..util.fit import PoorFit
 from ..util.peak import find_peaks, find_peaks_arguments, Peak, _kwargs
 from . import Plugin, argument_to_setting
@@ -149,9 +148,6 @@ class ConvolutionPeaksCommand (Command):
         deflection arrays.
         """
         curve = params['curve']
-        if not isinstance(curve.info['experiment'], VelocityClamp):
-            raise Failure('%s operates on VelocityClamp experiments, not %s'
-                          % (self.name, curve.info['experiment']))
         data = None
         for block in curve.data:
             if block.info['name'].startswith('retract'):
index dbdace9..3ef141c 100644 (file)
@@ -292,9 +292,8 @@ class InfoCommand (CurveCommand):
             Argument(name='all', type='bool', default=False, count=1,
                      help='Get all curve information.'),
             ]
-        self.fields = ['name', 'path', 'experiment', 'driver', 'filetype',
-                       'note', 'command stack', 'blocks', 'block names',
-                       'block sizes']
+        self.fields = ['name', 'path', 'driver', 'note', 'command stack',
+                       'blocks', 'block names', 'block sizes']
         for field in self.fields:
             args.append(Argument(
                     name=field, type='bool', default=False, count=1,
@@ -326,15 +325,9 @@ class InfoCommand (CurveCommand):
     def _get_path(self, curve):
         return curve.path
 
-    def _get_experiment(self, curve):
-        return curve.info.get('experiment', None)
-
     def _get_driver(self, curve):
         return curve.driver
 
-    def _get_filetype(self, curve):
-        return curve.info.get('filetype', None)
-
     def _get_note(self, curve):
         return curve.info.get('note', None)
                               
index 21665fe..44f0e1f 100644 (file)
@@ -28,8 +28,7 @@ several associated :class:`~hooke.command.Command`\s for removing flat
 
 See Also
 --------
-:mod:`~hooke.plugin.convfilt` for a convolution-based filter for
-:class:`~hooke.experiment.VelocityClamp` experiments.
+:mod:`~hooke.plugin.convfilt` for a convolution-based filter.
 """
 
 import copy
@@ -41,7 +40,6 @@ from scipy.signal.signaltools import medfilt
 from ..command import Argument, Success, Failure, UncaughtException
 from ..config import Setting
 from ..curve import Data
-from ..experiment import VelocityClamp
 from ..util.fit import PoorFit
 from ..util.peak import (find_peaks, peaks_to_mask,
                          find_peaks_arguments, Peak, _kwargs)
@@ -170,9 +168,6 @@ Name for storing the list of peaks in the `.info` dictionary.
         deflection arrays.
         """
         curve = self._curve(hooke=hooke, params=params)
-        if not isinstance(curve.info['experiment'], VelocityClamp):
-            raise Failure('%s operates on VelocityClamp experiments, not %s'
-                          % (self.name, curve.info['experiment']))
         for key,value in params.items():
             if value == None and key in self.plugin.config:
                 # Use configured default value.