Merged my unitary FFT wrappers (FFT_tools) as hooke.util.fft.
[hooke.git] / hooke / plugin / curve.py
1 # Copyright (C) 2008-2010 Alberto Gomez-Casado
2 #                         Fabrizio Benedetti
3 #                         Massimo Sandal <devicerandom@gmail.com>
4 #                         W. Trevor King <wking@drexel.edu>
5 #
6 # This file is part of Hooke.
7 #
8 # Hooke is free software: you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation, either
11 # version 3 of the License, or (at your option) any later version.
12 #
13 # Hooke is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with Hooke.  If not, see
20 # <http://www.gnu.org/licenses/>.
21
22 """The ``curve`` module provides :class:`CurvePlugin` and several
23 associated :class:`hooke.command.Command`\s for handling
24 :mod:`hooke.curve` classes.
25 """
26
27 from ..command import Command, Argument, Failure
28 from ..curve import Data
29 from ..plugin import Builtin
30 from ..plugin.playlist import current_playlist_callback
31 from ..util.calculus import derivative
32
33
34 class CurvePlugin (Builtin):
35     def __init__(self):
36         super(CurvePlugin, self).__init__(name='curve')
37
38     def commands(self):
39         return [InfoCommand(), ExportCommand()]
40
41
42 # Define common or complicated arguments
43
44 def current_curve_callback(hooke, command, argument, value):
45     if value != None:
46         return value
47     playlist = current_playlist_callback(hooke, command, argument, value)
48     curve = playlist.current()
49     if curve == None:
50         raise Failure('No curves in %s' % playlist)
51     return curve
52
53 CurveArgument = Argument(
54     name='curve', type='curve', callback=current_curve_callback,
55     help="""
56 :class:`hooke.curve.Curve` to act on.  Defaults to the current curve
57 of the current playlist.
58 """.strip())
59
60
61 # Define commands
62
63 class InfoCommand (Command):
64     """Get selected information about a :class:`hooke.curve.Curve`.
65     """
66     def __init__(self):
67         args = [
68             CurveArgument,                    
69             Argument(name='all', type='bool', default=False, count=1,
70                      help='Get all curve information.'),
71             ]
72         self.fields = ['name', 'path', 'experiment', 'driver', 'filetype', 'note',
73                        'blocks', 'block sizes']
74         for field in self.fields:
75             args.append(Argument(
76                     name=field, type='bool', default=False, count=1,
77                     help='Get curve %s' % field))
78         super(InfoCommand, self).__init__(
79             name='curve info', arguments=args, help=self.__doc__)
80
81     def _run(self, hooke, inqueue, outqueue, params):
82         fields = {}
83         for key in self.fields:
84             fields[key] = params[key]
85         if reduce(lambda x,y: x and y, fields.values()) == False:
86             params['all'] = True # No specific fields set, default to 'all'
87         if params['all'] == True:
88             for key in self.fields:
89                 fields[key] = True
90         lines = []
91         for key in self.fields:
92             if fields[key] == True:
93                 get = getattr(self, '_get_%s' % key.replace(' ', '_'))
94                 lines.append('%s: %s' % (key, get(params['curve'])))
95         outqueue.put('\n'.join(lines))
96
97     def _get_name(self, curve):
98         return curve.name
99
100     def _get_path(self, curve):
101         return curve.path
102
103     def _get_experiment(self, curve):
104         return curve.info.get('experiment', None)
105
106     def _get_driver(self, curve):
107         return curve.driver
108
109     def _get_filetype(self, curve):
110         return curve.info.get('filetype', None)
111
112     def _get_note(self, curve):
113         return curve.info.get('note', None)
114                               
115     def _get_blocks(self, curve):
116         return len(curve.data)
117
118     def _get_block_sizes(self, curve):
119         return [block.shape for block in curve.data]
120
121 class ExportCommand (Command):
122     """Export a :class:`hooke.curve.Curve` data block as TAB-delimeted
123     ASCII text.
124     """
125     def __init__(self):
126         super(ExportCommand, self).__init__(
127             name='export block',
128             arguments=[
129                 CurveArgument,
130                 Argument(name='block', aliases=['set'], type='int', default=0,
131                          help="""
132 Data block to save.  For an approach/retract force curve, `0` selects
133 the approacing curve and `1` selects the retracting curve.
134 """.strip()),
135                 Argument(name='output', type='file', default='curve.dat',
136                          help="""
137 File name for the output data.  Defaults to 'curve.dat'
138 """.strip()),
139                 ],
140             help=self.__doc__)
141
142     def _run(self, hooke, inqueue, outqueue, params):
143         data = params['curve'].data[params['block']]
144         f = open(params['output'], 'w')
145         data.tofile(f, sep='\t')
146         f.close()
147
148 class DifferenceCommand (Command):
149     """Calculate the derivative (actually, the discrete differentiation)
150     of a curve data block.
151
152     See :func:`hooke.util.calculus.derivative` for implementation
153     details.
154     """
155     def __init__(self):
156         super(DifferenceCommand, self).__init__(
157             name='block difference',
158             arguments=[
159                 CurveArgument,
160                 Argument(name='block one', aliases=['set one'], type='int',
161                          default=1,
162                          help="""
163 Block A in A-B.  For an approach/retract force curve, `0` selects the
164 approacing curve and `1` selects the retracting curve.
165 """.strip()),
166                 Argument(name='block two', aliases=['set two'], type='int',
167                          default=0,
168                          help='Block B in A-B.'),
169                 Argument(name='x column', type='int', default=0,
170                          help="""
171 Column of data block to differentiate with respect to.
172 """.strip()),
173                 Argument(name='y column', type='int', default=1,
174                          help="""
175 Column of data block to differentiate.
176 """.strip()),
177                 ],
178             help=self.__doc__)
179
180     def _run(self, hooke, inqueue, outqueue, params):
181         a = params['curve'].data[params['block one']]
182         b = params['curve'].data[params['block two']]
183         assert a[:,params['x column']] == b[:,params['x column']]:
184         out = Data((a.shape[0],2), dtype=a.dtype)
185         out[:,0] = a[:,params['x column']]
186         out[:,1] = a[:,params['y column']] - b[:,params['y column']]:
187         outqueue.put(out)
188
189 class DerivativeCommand (Command):
190     """Calculate the difference between two blocks of data.
191     """
192     def __init__(self):
193         super(DerivativeCommand, self).__init__(
194             name='block derivative',
195             arguments=[
196                 CurveArgument,
197                 Argument(name='block', aliases=['set'], type='int', default=0,
198                          help="""
199 Data block to differentiate.  For an approach/retract force curve, `0`
200 selects the approacing curve and `1` selects the retracting curve.
201 """.strip()),
202                 Argument(name='x column', type='int', default=0,
203                          help="""
204 Column of data block to differentiate with respect to.
205 """.strip()),
206                 Argument(name='y column', type='int', default=1,
207                          help="""
208 Column of data block to differentiate.
209 """.strip()),
210                 Argument(name='weights', type='dict', default={-1:-0.5, 1:0.5},
211                          help="""
212 Weighting scheme dictionary for finite differencing.  Defaults to
213 central differencing.
214 """.strip()),
215                 ],
216             help=self.__doc__)
217
218     def _run(self, hooke, inqueue, outqueue, params):
219         data = params['curve'].data[params['block']]
220         outqueue.put(derivative(
221                 block, x_col=params['x column'], y_col=params['y column'],
222                 weights=params['weights']))