X-Git-Url: http://git.tremily.us/?p=hooke.git;a=blobdiff_plain;f=hooke%2Fplugin%2Fcurve.py;h=bf219180721bc8e88755c2a5d9f70d51a025ba30;hp=3868a83db9fc5dff8028afb9fcde5e335cf08c08;hb=8273b2acd0162fd19e79cf52ab3822454d5b2c50;hpb=bff2e146f66541f8935c634b93aa4bf4d74b04ca diff --git a/hooke/plugin/curve.py b/hooke/plugin/curve.py index 3868a83..bf21918 100644 --- a/hooke/plugin/curve.py +++ b/hooke/plugin/curve.py @@ -1,4 +1,5 @@ # Copyright (C) 2008-2010 Alberto Gomez-Casado +# Fabrizio Benedetti # Massimo Sandal # W. Trevor King # @@ -18,285 +19,204 @@ # License along with Hooke. If not, see # . -"""Processed plots plugin for force curves. +"""The ``curve`` module provides :class:`CurvePlugin` and several +associated :class:`hooke.command.Command`\s for handling +:mod:`hooke.curve` classes. """ -from ..libhooke import WX_GOOD -import wxversion -wxversion.select(WX_GOOD) - -import wx -import numpy as np -import scipy as sp -import scipy.signal -import copy - -from .. import curve as lhc - - -class procplotsCommands(object): - - def _plug_init(self): - pass - - def do_derivplot(self,args): - ''' - DERIVPLOT - (procplots.py plugin) - Plots the derivate (actually, the discrete differentiation) of the current force curve - --------- - Syntax: derivplot - ''' - dplot=self.derivplot_curves() - plot_graph=self.list_of_events['plot_graph'] - wx.PostEvent(self.frame,plot_graph(plots=[dplot])) - - def derivplot_curves(self): - ''' - do_derivplot helper function - - create derivate plot curves for force curves. - ''' - dplot=lhc.PlotObject() - dplot.vectors=[] - - for vector in self.plots[0].vectors: - dplot.vectors.append([]) - dplot.vectors[-1].append(vector[0][:-1]) - dplot.vectors[-1].append(np.diff(vector[1])) - - dplot.destination=1 - dplot.units=self.plots[0].units - - return dplot - - def do_subtplot(self, args): - ''' - SUBTPLOT - (procplots.py plugin) - Plots the difference between ret and ext current curve - ------- - Syntax: subtplot - ''' - #FIXME: sub_filter and sub_order must be args - - if len(self.plots[0].vectors) != 2: - print 'This command only works on a curve with two different plots.' - pass - - outplot=self.subtract_curves(sub_order=1) - - plot_graph=self.list_of_events['plot_graph'] - wx.PostEvent(self.frame,plot_graph(plots=[outplot])) - - def subtract_curves(self, sub_order): - ''' - subtracts the extension from the retraction - ''' - xext=self.plots[0].vectors[0][0] - yext=self.plots[0].vectors[0][1] - xret=self.plots[0].vectors[1][0] - yret=self.plots[0].vectors[1][1] - - #we want the same number of points - maxpoints_tot=min(len(xext),len(xret)) - xext=xext[0:maxpoints_tot] - yext=yext[0:maxpoints_tot] - xret=xret[0:maxpoints_tot] - yret=yret[0:maxpoints_tot] - - if sub_order: - ydiff=[yretval-yextval for yretval,yextval in zip(yret,yext)] - else: #reverse subtraction (not sure it's useful, but...) - ydiff=[yextval-yretval for yextval,yretval in zip(yext,yret)] - - outplot=copy.deepcopy(self.plots[0]) - outplot.vectors[0][0], outplot.vectors[1][0] = xext,xret #FIXME: if I use xret, it is not correct! - outplot.vectors[1][1]=ydiff - outplot.vectors[0][1]=[0 for item in outplot.vectors[1][0]] - - return outplot - - -#-----PLOT MANIPULATORS - def plotmanip_median(self, plot, current, customvalue=None): - ''' - does the median of the y values of a plot - ''' - if customvalue: - median_filter=customvalue - else: - median_filter=self.config['medfilt'] - - if median_filter==0: - return plot - - if float(median_filter)/2 == int(median_filter)/2: - median_filter+=1 - - nplots=len(plot.vectors) - c=0 - while cstartvalue: - cutindex=index - else: - break - - plot.vectors[0][0]=plot.vectors[0][0][:cutindex] - plot.vectors[0][1]=plot.vectors[0][1][:cutindex] - - return plot - ''' - - - -#FFT--------------------------- - def fft_plot(self, vector): - ''' - calculates the fast Fourier transform for the selected vector in the plot - ''' - fftplot=lhc.PlotObject() - fftplot.vectors=[[]] - - fftlen=len(vector)/2 #need just 1/2 of length - fftplot.vectors[-1].append(np.arange(1,fftlen).tolist()) - - try: - fftplot.vectors[-1].append(abs(np.fft(vector)[1:fftlen]).tolist()) - except TypeError: #we take care of newer NumPy (1.0.x) - fftplot.vectors[-1].append(abs(np.fft.fft(vector)[1:fftlen]).tolist()) - - - fftplot.destination=1 - - - return fftplot - - - def do_fft(self,args): - ''' - FFT - (procplots.py plugin) - Plots the fast Fourier transform of the selected plot - --- - Syntax: fft [top,bottom] [select] [0,1...] - - By default, fft performs the Fourier transform on all the 0-th data set on the - top plot. - - [top,bottom]: which plot is the data set to fft (default: top) - [select]: you pick up two points on the plot and fft only the segment between - [0,1,...]: which data set on the selected plot you want to fft (default: 0) - ''' - - #args parsing - #whatplot = plot to fft - #whatset = set to fft in the plot - select=('select' in args) - if 'top' in args: - whatplot=0 - elif 'bottom' in args: - whatplot=1 - else: - whatplot=0 - whatset=0 - for arg in args: - try: - whatset=int(arg) - except ValueError: - pass - - if select: - points=self._measure_N_points(N=2, whatset=whatset) - boundaries=[points[0].index, points[1].index] - boundaries.sort() - y_to_fft=self.plots[whatplot].vectors[whatset][1][boundaries[0]:boundaries[1]] #y - else: - y_to_fft=self.plots[whatplot].vectors[whatset][1] #y - - fftplot=self.fft_plot(y_to_fft) - fftplot.units=['frequency', 'power'] - plot_graph=self.list_of_events['plot_graph'] - wx.PostEvent(self.frame,plot_graph(plots=[fftplot])) +from ..command import Command, Argument, Failure +from ..curve import Data +from ..plugin import Builtin +from ..plugin.playlist import current_playlist_callback +from ..util.calculus import derivative + + +class CurvePlugin (Builtin): + def __init__(self): + super(CurvePlugin, self).__init__(name='curve') + + def commands(self): + return [InfoCommand(), ExportCommand()] + + +# Define common or complicated arguments + +def current_curve_callback(hooke, command, argument, value): + if value != None: + return value + playlist = current_playlist_callback(hooke, command, argument, value) + curve = playlist.current() + if curve == None: + raise Failure('No curves in %s' % playlist) + return curve + +CurveArgument = Argument( + name='curve', type='curve', callback=current_curve_callback, + help=""" +:class:`hooke.curve.Curve` to act on. Defaults to the current curve +of the current playlist. +""".strip()) + + +# Define commands + +class InfoCommand (Command): + """Get selected information about a :class:`hooke.curve.Curve`. + """ + def __init__(self): + args = [ + CurveArgument, + Argument(name='all', type='bool', default=False, count=1, + help='Get all curve information.'), + ] + self.fields = ['name', 'path', 'experiment', 'driver', 'filetype', 'note', + 'blocks', 'block sizes'] + for field in self.fields: + args.append(Argument( + name=field, type='bool', default=False, count=1, + help='Get curve %s' % field)) + super(InfoCommand, self).__init__( + name='curve info', arguments=args, help=self.__doc__) + + def _run(self, hooke, inqueue, outqueue, params): + fields = {} + for key in self.fields: + fields[key] = params[key] + if reduce(lambda x,y: x and y, fields.values()) == False: + params['all'] = True # No specific fields set, default to 'all' + if params['all'] == True: + for key in self.fields: + fields[key] = True + lines = [] + for key in self.fields: + if fields[key] == True: + get = getattr(self, '_get_%s' % key.replace(' ', '_')) + lines.append('%s: %s' % (key, get(params['curve']))) + outqueue.put('\n'.join(lines)) + + def _get_name(self, curve): + return curve.name + + 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) + + def _get_blocks(self, curve): + return len(curve.data) + + def _get_block_sizes(self, curve): + return [block.shape for block in curve.data] + +class ExportCommand (Command): + """Export a :class:`hooke.curve.Curve` data block as TAB-delimeted + ASCII text. + """ + def __init__(self): + super(ExportCommand, self).__init__( + name='export block', + arguments=[ + CurveArgument, + Argument(name='block', aliases=['set'], type='int', default=0, + help=""" +Data block to save. For an approach/retract force curve, `0` selects +the approacing curve and `1` selects the retracting curve. +""".strip()), + Argument(name='output', type='file', default='curve.dat', + help=""" +File name for the output data. Defaults to 'curve.dat' +""".strip()), + ], + help=self.__doc__) + + def _run(self, hooke, inqueue, outqueue, params): + data = params['curve'].data[params['block']] + f = open(params['output'], 'w') + data.tofile(f, sep='\t') + f.close() + +class DifferenceCommand (Command): + """Calculate the derivative (actually, the discrete differentiation) + of a curve data block. + + See :func:`hooke.util.calculus.derivative` for implementation + details. + """ + def __init__(self): + super(DifferenceCommand, self).__init__( + name='block difference', + arguments=[ + CurveArgument, + Argument(name='block one', aliases=['set one'], type='int', + default=1, + help=""" +Block A in A-B. For an approach/retract force curve, `0` selects the +approacing curve and `1` selects the retracting curve. +""".strip()), + Argument(name='block two', aliases=['set two'], type='int', + default=0, + help='Block B in A-B.'), + Argument(name='x column', type='int', default=0, + help=""" +Column of data block to differentiate with respect to. +""".strip()), + Argument(name='y column', type='int', default=1, + help=""" +Column of data block to differentiate. +""".strip()), + ], + help=self.__doc__) + + def _run(self, hooke, inqueue, outqueue, params): + a = params['curve'].data[params['block one']] + b = params['curve'].data[params['block two']] + assert a[:,params['x column']] == b[:,params['x column']]: + out = Data((a.shape[0],2), dtype=a.dtype) + out[:,0] = a[:,params['x column']] + out[:,1] = a[:,params['y column']] - b[:,params['y column']]: + outqueue.put(out) + +class DerivativeCommand (Command): + """Calculate the difference between two blocks of data. + """ + def __init__(self): + super(DerivativeCommand, self).__init__( + name='block derivative', + arguments=[ + CurveArgument, + Argument(name='block', aliases=['set'], type='int', default=0, + help=""" +Data block to differentiate. For an approach/retract force curve, `0` +selects the approacing curve and `1` selects the retracting curve. +""".strip()), + Argument(name='x column', type='int', default=0, + help=""" +Column of data block to differentiate with respect to. +""".strip()), + Argument(name='y column', type='int', default=1, + help=""" +Column of data block to differentiate. +""".strip()), + Argument(name='weights', type='dict', default={-1:-0.5, 1:0.5}, + help=""" +Weighting scheme dictionary for finite differencing. Defaults to +central differencing. +""".strip()), + ], + help=self.__doc__) + + def _run(self, hooke, inqueue, outqueue, params): + data = params['curve'].data[params['block']] + outqueue.put(derivative( + block, x_col=params['x column'], y_col=params['y column'], + weights=params['weights']))