66199671ef0416bba514148621dc85775f835961
[hooke.git] / hooke / curve.py
1 # Copyright
2
3 """The `curve` module provides :class:`Curve` and :class:`Data` for
4 storing force curves.
5 """
6
7 import os.path
8 import numpy
9
10
11 class NotRecognized (ValueError):
12     def __init__(self, curve):
13         msg = 'Not a recognizable curve format: %s' % curve.path
14         ValueError.__init__(self, msg)
15         self.curve = curve
16
17 class Data (numpy.ndarray):
18     """Stores a single, continuous data set.
19
20     Adds :attr:`info` :class:`dict` to the standard :class:`numpy.ndarray`.
21
22     See :mod:`numpy.doc.subclassing` for the peculiarities of
23     subclassing :class:`numpy.ndarray`.
24     """
25     def __new__(self, subtype, shape, dtype=numpy.float, buffer=None, offset=0,
26                 strides=None, order=None, info=None):
27         """Create the ndarray instance of our type, given the usual
28         input arguments.  This will call the standard ndarray
29         constructor, but return an object of our type.
30         """
31         obj = np.ndarray.__new__(subtype=subtype, shape=shape, dtype=dtype,
32                                  buffer=buffer, offset=offset, strides=strides,
33                                  order=order)
34         # add the new attribute to the created instance
35         if info == None:
36             info = {}
37         obj.info = info
38         # Finally, we must return the newly created object:
39         return obj
40
41     def __array_finalize__(self, obj):
42         """Set any extra attributes from the original object when
43         creating a new view object."""
44         # reset the attribute from passed original object
45         self.info = getattr(obj, 'info', {})
46         # We do not need to return anything
47
48
49 class Curve (object):
50     """A grouped set of :class:`Data` runs from the same file with metadata.
51
52     For an approach/retract force spectroscopy experiment, the group
53     would consist of the approach data and the retract data.  Metadata
54     would be the temperature, cantilever spring constant, etc.
55
56     Two important :attr:`info` settings are `filetype` and
57     `experiment`.  These are two strings that can be used by Hooke
58     commands/plugins to understand what they are looking at.
59
60     * `.info['filetype']` should contain the name of the exact
61       filetype defined by the driver (so that filetype-speciofic
62       commands can know if they're dealing with the correct filetype).
63     * `.info['experiment']` should contain an instance of a
64       :class:`hooke.experiment.Experiment` subclass to identify the
65       experiment type.  For example, various
66       :class:`hooke.driver.Driver`\s can read in force-clamp data, but
67       Hooke commands could like to know if they're looking at force
68       clamp data, regardless of their origin.
69     """
70     def __init__(self, path, info=None):
71         #the data dictionary contains: {name of data: list of data sets [{[x], [y]}]
72         self.path = path
73         self.driver = None
74         self.data = []
75         if info == None:
76             info = {}
77         self.info = info
78         self.name = os.path.basename(path)
79
80     def identify(self, drivers):
81         """Identify the appropriate :class:`hooke.driver.Driver` for
82         the curve file (`.path`).
83         """
84         for driver in drivers:
85             if driver.is_me(self.path):
86                 self.driver = driver # remember the working driver
87                 return
88         raise NotRecognized(self)
89
90     def load(self):
91         """Use the driver to read the curve into memory.
92         """
93         pass