Ran update_copyright.py, updating all the copyright blurbs and adding AUTHORS.
[hooke.git] / hooke / curve.py
1 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
2 #
3 # This file is part of Hooke.
4 #
5 # Hooke is free software: you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation, either
8 # version 3 of the License, or (at your option) any later version.
9 #
10 # Hooke is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with Hooke.  If not, see
17 # <http://www.gnu.org/licenses/>.
18
19 """The `curve` module provides :class:`Curve` and :class:`Data` for
20 storing force curves.
21 """
22
23 import os.path
24 import numpy
25
26
27 class NotRecognized (ValueError):
28     def __init__(self, curve):
29         msg = 'Not a recognizable curve format: %s' % curve.path
30         ValueError.__init__(self, msg)
31         self.curve = curve
32
33 class Data (numpy.ndarray):
34     """Stores a single, continuous data set.
35
36     Adds :attr:`info` :class:`dict` to the standard :class:`numpy.ndarray`.
37
38     See :mod:`numpy.doc.subclassing` for the peculiarities of
39     subclassing :class:`numpy.ndarray`.
40     """
41     def __new__(self, subtype, shape, dtype=numpy.float, buffer=None, offset=0,
42                 strides=None, order=None, info=None):
43         """Create the ndarray instance of our type, given the usual
44         input arguments.  This will call the standard ndarray
45         constructor, but return an object of our type.
46         """
47         obj = np.ndarray.__new__(subtype=subtype, shape=shape, dtype=dtype,
48                                  buffer=buffer, offset=offset, strides=strides,
49                                  order=order)
50         # add the new attribute to the created instance
51         if info == None:
52             info = {}
53         obj.info = info
54         # Finally, we must return the newly created object:
55         return obj
56
57     def __array_finalize__(self, obj):
58         """Set any extra attributes from the original object when
59         creating a new view object."""
60         # reset the attribute from passed original object
61         self.info = getattr(obj, 'info', {})
62         # We do not need to return anything
63
64
65 class Curve (object):
66     """A grouped set of :class:`Data` runs from the same file with metadata.
67
68     For an approach/retract force spectroscopy experiment, the group
69     would consist of the approach data and the retract data.  Metadata
70     would be the temperature, cantilever spring constant, etc.
71
72     Two important :attr:`info` settings are `filetype` and
73     `experiment`.  These are two strings that can be used by Hooke
74     commands/plugins to understand what they are looking at.
75
76     * `.info['filetype']` should contain the name of the exact
77       filetype defined by the driver (so that filetype-speciofic
78       commands can know if they're dealing with the correct filetype).
79     * `.info['experiment']` should contain an instance of a
80       :class:`hooke.experiment.Experiment` subclass to identify the
81       experiment type.  For example, various
82       :class:`hooke.driver.Driver`\s can read in force-clamp data, but
83       Hooke commands could like to know if they're looking at force
84       clamp data, regardless of their origin.
85     """
86     def __init__(self, path, info=None):
87         #the data dictionary contains: {name of data: list of data sets [{[x], [y]}]
88         self.path = path
89         self.driver = None
90         self.data = []
91         if info == None:
92             info = {}
93         self.info = info
94         self.name = os.path.basename(path)
95
96     def identify(self, drivers):
97         """Identify the appropriate :class:`hooke.driver.Driver` for
98         the curve file (`.path`).
99         """
100         for driver in drivers:
101             if driver.is_me(self.path):
102                 self.driver = driver # remember the working driver
103                 return
104         raise NotRecognized(self)
105
106     def load(self):
107         """Use the driver to read the curve into memory.
108         """
109         pass