From 30d5d93368e3170161f6a9d4f94dc8f0f958277a Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 7 Sep 2010 14:43:11 -0400 Subject: [PATCH] Don't specify curve or data block types. See doc/standards.txt. --- doc/analysis.txt | 85 +++++++++++++++++++++++++++++++++++++ doc/index.txt | 2 + doc/standards.txt | 63 +++++++++++++++++++++++++++ hooke/curve.py | 26 ++++-------- hooke/driver/__init__.py | 4 +- hooke/driver/csvdriver.py | 4 -- hooke/driver/hdf5.py | 3 -- hooke/driver/hemingway.py | 5 +-- hooke/driver/jpk.py | 9 ---- hooke/driver/mcs.py | 3 -- hooke/driver/mfp1d.py | 3 -- hooke/driver/mfp1dexport.py | 3 -- hooke/driver/mfp3d.py | 3 -- hooke/driver/picoforce.py | 3 -- hooke/driver/tutorial.py | 3 +- hooke/driver/wtk.py | 3 +- hooke/experiment.py | 64 ---------------------------- hooke/plugin/convfilt.py | 4 -- hooke/plugin/curve.py | 11 +---- hooke/plugin/flatfilt.py | 7 +-- 20 files changed, 165 insertions(+), 143 deletions(-) create mode 100644 doc/analysis.txt create mode 100644 doc/standards.txt delete mode 100644 hooke/experiment.py diff --git a/doc/analysis.txt b/doc/analysis.txt new file mode 100644 index 0000000..1286393 --- /dev/null +++ b/doc/analysis.txt @@ -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 `_ + +.. _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 `_ + +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 `_ diff --git a/doc/index.txt b/doc/index.txt index 37a1167..a067fb7 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -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 index 0000000..743b9aa --- /dev/null +++ b/doc/standards.txt @@ -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. diff --git a/hooke/curve.py b/hooke/curve.py index fc8701b..030efcb 100644 --- a/hooke/curve.py +++ b/hooke/curve.py @@ -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. diff --git a/hooke/driver/__init__.py b/hooke/driver/__init__.py index bc38a24..8704f5d 100644 --- a/hooke/driver/__init__.py +++ b/hooke/driver/__init__.py @@ -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 diff --git a/hooke/driver/csvdriver.py b/hooke/driver/csvdriver.py index 3ec4cbe..5713233 100644 --- a/hooke/driver/csvdriver.py +++ b/hooke/driver/csvdriver.py @@ -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): diff --git a/hooke/driver/hdf5.py b/hooke/driver/hdf5.py index f338324..64f09be 100644 --- a/hooke/driver/hdf5.py +++ b/hooke/driver/hdf5.py @@ -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() diff --git a/hooke/driver/hemingway.py b/hooke/driver/hemingway.py index af351f7..fe0fb98 100644 --- a/hooke/driver/hemingway.py +++ b/hooke/driver/hemingway.py @@ -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) diff --git a/hooke/driver/jpk.py b/hooke/driver/jpk.py index d70fa3c..8ca521d 100644 --- a/hooke/driver/jpk.py +++ b/hooke/driver/jpk.py @@ -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'] diff --git a/hooke/driver/mcs.py b/hooke/driver/mcs.py index 50a5029..5e80c51 100644 --- a/hooke/driver/mcs.py +++ b/hooke/driver/mcs.py @@ -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() diff --git a/hooke/driver/mfp1d.py b/hooke/driver/mfp1d.py index 2c314bd..355ba44 100644 --- a/hooke/driver/mfp1d.py +++ b/hooke/driver/mfp1d.py @@ -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') diff --git a/hooke/driver/mfp1dexport.py b/hooke/driver/mfp1dexport.py index 174696b..63f5851 100644 --- a/hooke/driver/mfp1dexport.py +++ b/hooke/driver/mfp1dexport.py @@ -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() diff --git a/hooke/driver/mfp3d.py b/hooke/driver/mfp3d.py index 3347e3d..cf71874 100644 --- a/hooke/driver/mfp3d.py +++ b/hooke/driver/mfp3d.py @@ -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): diff --git a/hooke/driver/picoforce.py b/hooke/driver/picoforce.py index ca84792..625c455 100644 --- a/hooke/driver/picoforce.py +++ b/hooke/driver/picoforce.py @@ -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): diff --git a/hooke/driver/tutorial.py b/hooke/driver/tutorial.py index 6856f8d..abd2dae 100644 --- a/hooke/driver/tutorial.py +++ b/hooke/driver/tutorial.py @@ -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) diff --git a/hooke/driver/wtk.py b/hooke/driver/wtk.py index 4124701..031fb80 100644 --- a/hooke/driver/wtk.py +++ b/hooke/driver/wtk.py @@ -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 index 646b415..0000000 --- a/hooke/experiment.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (C) 2010 W. Trevor King -# -# 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 -# . - -"""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 `_ - """ - 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 `_ - """ - 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 `_ - """ - pass diff --git a/hooke/plugin/convfilt.py b/hooke/plugin/convfilt.py index a5780e9..a741f69 100644 --- a/hooke/plugin/convfilt.py +++ b/hooke/plugin/convfilt.py @@ -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'): diff --git a/hooke/plugin/curve.py b/hooke/plugin/curve.py index dbdace9..3ef141c 100644 --- a/hooke/plugin/curve.py +++ b/hooke/plugin/curve.py @@ -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) diff --git a/hooke/plugin/flatfilt.py b/hooke/plugin/flatfilt.py index 21665fe..44f0e1f 100644 --- a/hooke/plugin/flatfilt.py +++ b/hooke/plugin/flatfilt.py @@ -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. -- 2.26.2