From 7546f90e6dca8da41f90e4be986be685ba71ee1d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 11 Aug 2010 16:25:47 -0400 Subject: [PATCH] Updated vclamp to use new ColumnAddingCommand. Also added 'ignore after last peak info name' argument to SurfaceContactCommand" This allows for the two-cycle surface detection mentioned in commit 725a18055518. ./bin/hooke -c 'load_playlist test/data/vclamp_wtk/playlist' -c 'zero_surface_contact_point --block retract' -c 'flat_filter_peaks --block retract --min_points 1' -c 'zero_surface_contact_point --block retract --ignore_after_last_peak_info_name "flat filter peaks"' -c 'convert_distance_to_force --block retract' -c 'remove_cantilever_from_extension --block retract' -c 'flat_peaks_to_polymer_peaks --block retract' -c 'polymer_fit_peaks --block retract' --command-no-exit Note that `flat filter peaks`, `flat peaks to polymer peaks`, and `polymer fit peaks` still need to be updated to ColumnAddingCommand subclasses. Also, I might be missing a few column renames in the above command. I'll include a working (and tested) command once I get the other plugins up to speed with ColumnAddingCommand. --- hooke/plugin/vclamp.py | 445 ++++++++++------------- test/convert_distance_to_force.py | 45 +++ test/polynomial_flatten.py | 46 +++ test/remove_cantilever_from_extension.py | 44 +++ test/zero_surface_contact_point.py | 49 +++ 5 files changed, 378 insertions(+), 251 deletions(-) create mode 100644 test/convert_distance_to_force.py create mode 100644 test/polynomial_flatten.py create mode 100644 test/remove_cantilever_from_extension.py create mode 100644 test/zero_surface_contact_point.py diff --git a/hooke/plugin/vclamp.py b/hooke/plugin/vclamp.py index 48d55b3..77e3387 100644 --- a/hooke/plugin/vclamp.py +++ b/hooke/plugin/vclamp.py @@ -31,50 +31,13 @@ import logging import numpy import scipy -from ..command import Command, Argument, Failure, NullQueue +from ..command import Argument, Failure, NullQueue from ..config import Setting from ..curve import Data from ..plugin import Plugin from ..util.fit import PoorFit, ModelFitter from ..util.si import join_data_label, split_data_label -from .curve import CurveArgument - - -def scale(hooke, curve, block=None): - """Run 'add block force array' on `block`. - - Runs 'zero block surface contact point' first, if necessary. Does - not run either command if the columns they add to the block are - already present. - - If `block` is `None`, scale all blocks in `curve`. - """ - commands = hooke.commands - contact = [c for c in hooke.commands - if c.name == 'zero block surface contact point'][0] - force = [c for c in hooke.commands if c.name == 'add block force array'][0] - cant_adjust = [c for c in hooke.commands - if c.name == 'add block cantilever adjusted extension array'][0] - inqueue = None - outqueue = NullQueue() - if block == None: - for i in range(len(curve.data)): - scale(hooke, curve, block=i) - else: - params = {'curve':curve, 'block':block} - b = curve.data[block] - if ('surface distance (m)' not in b.info['columns'] - or 'surface deflection (m)' not in b.info['columns']): - try: - contact.run(hooke, inqueue, outqueue, **params) - except PoorFit, e: - raise PoorFit('Could not fit %s %s: %s' - % (curve.path, block, str(e))) - if ('deflection (N)' not in b.info['columns']): - force.run(hooke, inqueue, outqueue, **params) - if ('cantilever adjusted extension (m)' not in b.info['columns']): - cant_adjust.run(hooke, inqueue, outqueue, **params) - return curve +from .curve import ColumnAddingCommand class SurfacePositionModel (ModelFitter): @@ -236,7 +199,7 @@ class VelocityClampPlugin (Plugin): ] -class SurfaceContactCommand (Command): +class SurfaceContactCommand (ColumnAddingCommand): """Automatically determine a block's surface contact point. You can select the contact point algorithm with the creatively @@ -249,39 +212,34 @@ class SurfaceContactCommand (Command): """ def __init__(self, plugin): super(SurfaceContactCommand, self).__init__( - name='zero block surface contact point', - arguments=[ - CurveArgument, - Argument(name='block', aliases=['set'], type='int', default=0, - help=""" -Data block for which the force should be calculated. For an -approach/retract force curve, `0` selects the approaching curve and `1` -selects the retracting curve. -""".strip()), - Argument(name='ignore index', type='int', default=None, - help=""" -Ignore the residual from the non-contact region before the indexed -point (for the `wtk` algorithm). -""".strip()), - Argument(name='input distance column', type='string', - default='z piezo (m)', - help=""" + name='zero surface contact point', + columns=[ + ('distance column', 'z piezo (m)', """ Name of the column to use as the surface position input. """.strip()), - Argument(name='input deflection column', type='string', - default='deflection (m)', - help=""" + ('deflection column', 'deflection (m)', """ Name of the column to use as the deflection input. """.strip()), - Argument(name='output distance column', type='string', - default='surface distance', - help=""" + ], + new_columns=[ + ('output distance column', 'surface distance', """ Name of the column (without units) to use as the surface position output. """.strip()), - Argument(name='output deflection column', type='string', - default='surface deflection', - help=""" + ('output deflection column', 'surface deflection', """ Name of the column (without units) to use as the deflection output. +""".strip()), + ], + arguments=[ + Argument(name='ignore index', type='int', default=None, + help=""" +Ignore the residual from the non-contact region before the indexed +point (for the `wtk` algorithm). +""".strip()), + Argument(name='ignore after last peak info name', + type='string', default=None, + help=""" +As an alternative to 'ignore index', ignore after the last peak in the +peak list stored in the `.info` dictionary. """.strip()), Argument(name='distance info name', type='string', default='surface distance offset', @@ -302,36 +260,37 @@ Name (without units) for storing fit parameters in the `.info` dictionary. help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - data = params['curve'].data[params['block']] - # HACK? rely on params['curve'] being bound to the local hooke - # playlist (i.e. not a copy, as you would get by passing a - # curve through the queue). Ugh. Stupid queues. As an - # alternative, we could pass lookup information through the - # queue... - new = Data((data.shape[0], data.shape[1]+2), dtype=data.dtype) - new.info = copy.deepcopy(data.info) - new[:,:-2] = data - name,dist_units = split_data_label(params['input distance column']) - name,def_units = split_data_label(params['input deflection column']) - new.info['columns'].extend([ - join_data_label(params['output distance column'], dist_units), - join_data_label(params['output deflection column'], def_units), - ]) - dist_data = data[:,data.info['columns'].index( - params['input distance column'])] - def_data = data[:,data.info['columns'].index( - params['input deflection column'])] + params = self.__setup_params(hooke=hooke, params=params) + block = self._block(hooke=hooke, params=params) + dist_data = self._get_column(hooke=hooke, params=params, + column_name='distance column') + def_data = self._get_column(hooke=hooke, params=params, + column_name='deflection column') i,def_offset,ps = self.find_contact_point( params, dist_data, def_data, outqueue) dist_offset = dist_data[i] - new.info[join_data_label(params['distance info name'], dist_units - )] = dist_offset - new.info[join_data_label(params['deflection info name'], def_units - )] = def_offset - new.info[params['fit parameters info name']] = ps - new[:,-2] = dist_data - dist_offset - new[:,-1] = def_data - def_offset - params['curve'].data[params['block']] = new + block.info[params['distance info name']] = dist_offset + block.info[params['deflection info name']] = def_offset + block.info[params['fit parameters info name']] = ps + self._set_column(hooke=hooke, params=params, + column_name='output distance column', + values=dist_data - dist_offset) + self._set_column(hooke=hooke, params=params, + column_name='output deflection column', + values=def_data - def_offset) + + def __setup_params(self, hooke, params): + name,dist_unit = split_data_label(params['distance column']) + name,def_unit = split_data_label(params['deflection column']) + params['output distance column'] = join_data_label( + params['output distance column'], dist_unit) + params['output deflection column'] = join_data_label( + params['output deflection column'], def_unit) + params['distance info name'] = join_data_label( + params['distance info name'], dist_unit) + params['deflection info name'] = join_data_label( + params['deflection info name'], def_unit) + return params def find_contact_point(self, params, z_data, d_data, outqueue=None): """Railyard for the `find_contact_point_*` family. @@ -462,8 +421,18 @@ Name (without units) for storing fit parameters in the `.info` dictionary. s = SurfacePositionModel(d_data, info={ 'force zero non-contact slope':True}, rescale=True) + ignore_index = None if params['ignore index'] != None: - s.info['ignore non-contact before index'] = params['ignore index'] + ignore_index = params['ignore index'] + elif params['ignore after last peak info name'] != None: + peaks = z_data.info[params['ignore after last peak info name']] + if not len(peaks) > 0: + raise Failure('Need at least one peak in %s, not %s' + % (params['ignore after last peak info name'], + peaks)) + ignore_index = peaks[-1].post_index() + if ignore_index != None: + s.info['ignore non-contact before index'] = ignore_index offset,contact_slope,surface_index,non_contact_slope = s.fit( outqueue=outqueue) info = { @@ -479,30 +448,23 @@ Name (without units) for storing fit parameters in the `.info` dictionary. return (numpy.round(surface_index), deflection_offset, info) -class ForceCommand (Command): +class ForceCommand (ColumnAddingCommand): """Convert a deflection column from meters to newtons. """ def __init__(self, plugin): super(ForceCommand, self).__init__( - name='add block force array', - arguments=[ - CurveArgument, - Argument(name='block', aliases=['set'], type='int', default=0, - help=""" -Data block for which the force should be calculated. For an -approach/retract force curve, `0` selects the approaching curve and `1` -selects the retracting curve. -""".strip()), - Argument(name='input deflection column', type='string', - default='surface deflection (m)', - help=""" + name='convert distance to force', + columns=[ + ('deflection column', 'surface deflection (m)', """ Name of the column to use as the deflection input. """.strip()), - Argument(name='output deflection column', type='string', - default='deflection', - help=""" + ], + new_columns=[ + ('output deflection column', 'deflection', """ Name of the column (without units) to use as the deflection output. """.strip()), + ], + arguments=[ Argument(name='spring constant info name', type='string', default='spring constant (N/m)', help=""" @@ -512,52 +474,54 @@ Name of the spring constant in the `.info` dictionary. help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - data = params['curve'].data[params['block']] - # HACK? rely on params['curve'] being bound to the local hooke - # playlist (i.e. not a copy, as you would get by passing a - # curve through the queue). Ugh. Stupid queues. As an - # alternative, we could pass lookup information through the - # queue. - new = Data((data.shape[0], data.shape[1]+1), dtype=data.dtype) - new.info = copy.deepcopy(data.info) - new[:,:-1] = data - new.info['columns'].append( - join_data_label(params['output deflection column'], 'N')) - d_data = data[:,data.info['columns'].index( - params['input deflection column'])] - new[:,-1] = d_data * data.info[params['spring constant info name']] - params['curve'].data[params['block']] = new - - -class CantileverAdjustedExtensionCommand (Command): + params = self.__setup_params(hooke=hooke, params=params) + def_data = self._get_column(hooke=hooke, params=params, + column_name='deflection column') + out = def_data * def_data.info[params['spring constant info name']] + self._set_column(hooke=hooke, params=params, + column_name='output deflection column', + values=out) + + def __setup_params(self, hooke, params): + name,in_unit = split_data_label(params['deflection column']) + out_unit = 'N' # HACK: extract target units from k_unit. + params['output deflection column'] = join_data_label( + params['output deflection column'], out_unit) + name,k_unit = split_data_label(params['spring constant info name']) + expected_k_unit = '%s/%s' % (out_unit, in_unit) + if k_unit != expected_k_unit: + raise Failure('Cannot convert from %s to %s with %s' + % (params['deflection column'], + params['output deflection column'], + params['spring constant info name'])) + return params + + +class CantileverAdjustedExtensionCommand (ColumnAddingCommand): """Remove cantilever extension from a total extension column. + + If `distance column` and `deflection column` have the same units + (e.g. `z piezo (m)` and `deflection (m)`), `spring constant info + name` is ignored and a deflection/distance conversion factor of + one is used. """ def __init__(self, plugin): super(CantileverAdjustedExtensionCommand, self).__init__( - name='add block cantilever adjusted extension array', - arguments=[ - CurveArgument, - Argument(name='block', aliases=['set'], type='int', default=0, - help=""" -Data block for which the adjusted extension should be calculated. For -an approach/retract force curve, `0` selects the approaching curve and -`1` selects the retracting curve. -""".strip()), - Argument(name='input distance column', type='string', - default='surface distance (m)', - help=""" -Name of the column to use as the distance input. + name='remove cantilever from extension', + columns=[ + ('distance column', 'surface distance (m)', """ +Name of the column to use as the surface position input. """.strip()), - Argument(name='input deflection column', type='string', - default='deflection (N)', - help=""" + ('deflection column', 'deflection (N)', """ Name of the column to use as the deflection input. """.strip()), - Argument(name='output distance column', type='string', - default='cantilever adjusted extension', - help=""" -Name of the column (without units) to use as the deflection output. + ], + new_columns=[ + ('output distance column', 'cantilever adjusted extension', """ +Name of the column (without units) to use as the surface position output. """.strip()), + ], + arguments=[ Argument(name='spring constant info name', type='string', default='spring constant (N/m)', help=""" @@ -567,35 +531,38 @@ Name of the spring constant in the `.info` dictionary. help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - data = params['curve'].data[params['block']] - # HACK? rely on params['curve'] being bound to the local hooke - # playlist (i.e. not a copy, as you would get by passing a - # curve through the queue). Ugh. Stupid queues. As an - # alternative, we could pass lookup information through the - # queue. - new = Data((data.shape[0], data.shape[1]+1), dtype=data.dtype) - new.info = copy.deepcopy(data.info) - new[:,:-1] = data - new.info['columns'].append( - join_data_label(params['output distance column'], 'm')) - z_data = data[:,data.info['columns'].index( - params['input distance column'])] - d_data = data[:,data.info['columns'].index( - params['input deflection column'])] - k = data.info[params['spring constant info name']] - - z_name,z_unit = split_data_label(params['input distance column']) - assert z_unit == 'm', params['input distance column'] - d_name,d_unit = split_data_label(params['input deflection column']) - assert d_unit == 'N', params['input deflection column'] - k_name,k_unit = split_data_label(params['spring constant info name']) - assert k_unit == 'N/m', params['spring constant info name'] - - new[:,-1] = z_data - d_data / k - params['curve'].data[params['block']] = new - - -class FlattenCommand (Command): + params = self.__setup_params(hooke=hooke, params=params) + def_data = self._get_column(hooke=hooke, params=params, + column_name='deflection column') + dist_data = self._get_column(hooke=hooke, params=params, + column_name='distance column') + if params['spring constant info name'] == None: + k = 1.0 # distance and deflection in the same units + else: + k = def_data.info[params['spring constant info name']] + self._set_column(hooke=hooke, params=params, + column_name='output distance column', + values=dist_data - def_data / k) + + def __setup_params(self, hooke, params): + name,dist_unit = split_data_label(params['distance column']) + name,def_unit = split_data_label(params['deflection column']) + params['output distance column'] = join_data_label( + params['output distance column'], dist_unit) + if dist_unit == def_unit: + params['spring constant info name'] == None + else: + name,k_unit = split_data_label(params['spring constant info name']) + expected_k_unit = '%s/%s' % (def_unit, dist_unit) + if k_unit != expected_k_unit: + raise Failure('Cannot convert from %s to %s with %s' + % (params['deflection column'], + params['output distance column'], + params['spring constant info name'])) + return params + + +class FlattenCommand (ColumnAddingCommand): """Flatten a deflection column. Subtracts a polynomial fit from the non-contact part of the curve @@ -609,37 +576,27 @@ class FlattenCommand (Command): """ def __init__(self, plugin): super(FlattenCommand, self).__init__( - name='add flattened extension array', - arguments=[ - CurveArgument, - Argument(name='block', aliases=['set'], type='int', default=0, - help=""" -Data block for which the adjusted extension should be calculated. For -an approach/retract force curve, `0` selects the approaching curve and -`1` selects the retracting curve. -""".strip()), - Argument(name='max degree', type='int', - default=1, - help=""" -Highest order polynomial to consider for flattening. Using values -greater than one usually doesn't help and can give artifacts. -However, it could be useful too. (TODO: Back this up with some -theory...) -""".strip()), - Argument(name='input distance column', type='string', - default='surface distance (m)', - help=""" -Name of the column to use as the distance input. + name='polynomial flatten', + columns=[ + ('distance column', 'surface distance (m)', """ +Name of the column to use as the surface position input. """.strip()), - Argument(name='input deflection column', type='string', - default='deflection (N)', - help=""" + ('deflection column', 'deflection (N)', """ Name of the column to use as the deflection input. """.strip()), - Argument(name='output deflection column', type='string', - default='flattened deflection', - help=""" + ], + new_columns=[ + ('output deflection column', 'flattened deflection', """ Name of the column (without units) to use as the deflection output. +""".strip()), + ], + arguments=[ + Argument(name='degree', type='int', + default=1, + help=""" +Order of the polynomial used for flattening. Using values greater +than one usually doesn't help and can give artifacts. However, it +could be useful too. (TODO: Back this up with some theory...) """.strip()), Argument(name='fit info name', type='string', default='flatten fit', @@ -650,54 +607,40 @@ Name of the flattening information in the `.info` dictionary. help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - data = params['curve'].data[params['block']] - # HACK? rely on params['curve'] being bound to the local hooke - # playlist (i.e. not a copy, as you would get by passing a - # curve through the queue). Ugh. Stupid queues. As an - # alternative, we could pass lookup information through the - # queue. - new = Data((data.shape[0], data.shape[1]+1), dtype=data.dtype) - new.info = copy.deepcopy(data.info) - new[:,:-1] = data - z_data = data[:,data.info['columns'].index( - params['input distance column'])] - d_data = data[:,data.info['columns'].index( - params['input deflection column'])] - - d_name,d_unit = split_data_label(params['input deflection column']) - new.info['columns'].append( - join_data_label(params['output deflection column'], d_unit)) - - contact_index = numpy.absolute(z_data).argmin() - mask = z_data > 0 + params = self.__setup_params(hooke=hooke, params=params) + block = self._block(hooke=hooke, params=params) + dist_data = self._get_column(hooke=hooke, params=params, + column_name='distance column') + def_data = self._get_column(hooke=hooke, params=params, + column_name='deflection column') + degree = params['degree'] + mask = dist_data > 0 indices = numpy.argwhere(mask) - z_nc = z_data[indices].flatten() - d_nc = d_data[indices].flatten() - - min_err = numpy.inf - degree = poly_values = None - log = logging.getLogger('hooke') - for deg in range(params['max degree']): - try: - pv = scipy.polyfit(z_nc, d_nc, deg) - df = scipy.polyval(pv, z_nc) - err = numpy.sqrt((df-d_nc)**2).sum() - except Exception,e: - log.warn('failed to flatten with a degree %d polynomial: %s' - % (deg, e)) - continue - if err < min_err: # new best fit - min_err = err - degree = deg - poly_values = pv - - if degree == None: - raise Failure('failed to flatten with all degrees') - new.info[params['fit info name']] = { - 'error':min_err/len(z_nc), + if len(indices) == 0: + raise Failure('no positive distance values in %s' + % params['distance column']) + dist_nc = dist_data[indices].flatten() + def_nc = def_data[indices].flatten() + + try: + poly_values = scipy.polyfit(dist_nc, def_nc, degree) + def_nc_fit = scipy.polyval(poly_values, dist_nc) + except Exception, e: + raise Failure('failed to flatten with a degree %d polynomial: %s' + % (degree, e)) + error = numpy.sqrt((def_nc_fit-def_nc)**2).sum() / len(def_nc) + block.info[params['fit info name']] = { + 'error':error, 'degree':degree, - 'max degree':params['max degree'], 'polynomial values':poly_values, } - new[:,-1] = d_data - mask*scipy.polyval(poly_values, z_data) - params['curve'].data[params['block']] = new + out = def_data - mask*scipy.polyval(poly_values, dist_data) + self._set_column(hooke=hooke, params=params, + column_name='output deflection column', + values=out) + + def __setup_params(self, hooke, params): + d_name,d_unit = split_data_label(params['deflection column']) + params['output deflection column'] = join_data_label( + params['output deflection column'], d_unit) + return params diff --git a/test/convert_distance_to_force.py b/test/convert_distance_to_force.py new file mode 100644 index 0000000..03a024f --- /dev/null +++ b/test/convert_distance_to_force.py @@ -0,0 +1,45 @@ +# 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 +# . + +""" +>>> from hooke.hooke import Hooke, HookeRunner +>>> h = Hooke() +>>> r = HookeRunner() +>>> h = r.run_lines(h, ['load_playlist test/data/test']) # doctest: +ELLIPSIS + +Success + +>>> h = r.run_lines(h, +... ['convert_distance_to_force --deflection_column "deflection (m)"'] +... ) # doctest: +ELLIPSIS, +REPORT_UDIFF +Success + +>>> curve = h.playlists.current().current() +>>> approach = curve.data[0] +>>> approach.info['columns'] +['z piezo (m)', 'deflection (m)', 'deflection (N)'] +>>> approach[:5,-1] # doctest: +ELLIPSIS +Data([ 4.997...-09, 5.017...e-09, 5.032...e-09, + 5.049...e-09, 5.067...e-09]) +>>> approach[-5:,-1] # doctest: +ELLIPSIS +Data([ 6.987...e-09, 6.999...e-09, 6.979...e-09, + 6.962...e-09, 6.967...e-09]) + +Note that the rediculously high forces are because the deflection has +not been zeroed (e.g. with 'zero surface contact point'). +""" diff --git a/test/polynomial_flatten.py b/test/polynomial_flatten.py new file mode 100644 index 0000000..c23ca47 --- /dev/null +++ b/test/polynomial_flatten.py @@ -0,0 +1,46 @@ +# 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 +# . + +""" +>>> from hooke.hooke import Hooke, HookeRunner +>>> h = Hooke() +>>> r = HookeRunner() +>>> h = r.run_lines(h, ['load_playlist test/data/test']) # doctest: +ELLIPSIS + +Success + +>>> h = r.run_lines(h, +... ['polynomial_flatten --distance_column "z piezo (m)" --deflection_column "deflection (m)"'] +... ) # doctest: +ELLIPSIS, +REPORT_UDIFF +Success + +>>> curve = h.playlists.current().current() +>>> approach = curve.data[0] +>>> approach.info['columns'] +['z piezo (m)', 'deflection (m)', 'flattened deflection (m)'] +>>> approach[:5,-1] # doctest: +ELLIPSIS +Data([ 9.094...e-08, 9.130...e-08, 9.157...e-08, + 9.189...e-08, 9.221...e-08]) +>>> approach[-5:,-1] # doctest: +ELLIPSIS +Data([ 3.624...e-10, 5.884...e-10, 2.257...e-10, + -9.157...e-11, -9.027...e-13]) + +In practice you should zero your data before using +`polynomial flatten`, because it flattens all data with positive + deflection, and you don't want it to flatten the contact region. +""" diff --git a/test/remove_cantilever_from_extension.py b/test/remove_cantilever_from_extension.py new file mode 100644 index 0000000..d69d810 --- /dev/null +++ b/test/remove_cantilever_from_extension.py @@ -0,0 +1,44 @@ +# 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 +# . + +""" +>>> from hooke.hooke import Hooke, HookeRunner +>>> h = Hooke() +>>> r = HookeRunner() +>>> h = r.run_lines(h, ['load_playlist test/data/test']) # doctest: +ELLIPSIS + +Success + +>>> h = r.run_lines(h, +... ['remove_cantilever_from_extension --distance_column "z piezo (m)" --deflection_column "deflection (m)"'] +... ) +Success + +>>> curve = h.playlists.current().current() +>>> approach = curve.data[0] +>>> approach.info['columns'] +['z piezo (m)', 'deflection (m)', 'cantilever adjusted extension (m)'] +>>> approach[:5,-1] # doctest: +ELLIPSIS +Data([ -1.806...e-06, -1.812...e-06, -1.817...e-06, + -1.823...e-06, -1.828...e-06]) +>>> approach[-5:,-1] # doctest: +ELLIPSIS +Data([ -1.981...e-06, -1.986...e-06, -1.980...e-06, + -1.974...e-06, -1.975...e-06]) + +The large shift is due to the unzeroed input deflection. +""" diff --git a/test/zero_surface_contact_point.py b/test/zero_surface_contact_point.py new file mode 100644 index 0000000..d02ff11 --- /dev/null +++ b/test/zero_surface_contact_point.py @@ -0,0 +1,49 @@ +# 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 +# . + +""" +>>> from hooke.hooke import Hooke, HookeRunner +>>> h = Hooke() +>>> r = HookeRunner() +>>> h = r.run_lines(h, ['load_playlist test/data/test']) # doctest: +ELLIPSIS + +Success + +>>> h = r.run_lines(h, +... ['zero_surface_contact_point --block retract'] +... ) # doctest: +ELLIPSIS, +REPORT_UDIFF +{'info':...'fitted parameters': [8.413...e-08, 2.812...e-10, 158.581...],...} +Success + +>>> curve = h.playlists.current().current() +>>> retract = curve.data[1] +>>> retract.info['columns'] +['z piezo (m)', 'deflection (m)', 'surface distance (m)', 'surface deflection (m)'] +>>> retract[:5,-2:] # doctest: +ELLIPSIS +Data([[ -3.387...e-08, -4.1686...e-08], + [ -3.387...e-08, -4.161...e-08], + [ -3.356...e-08, -4.157...e-08], + [ -3.417...e-08, -4.161...e-08], + [ -3.387...e-08, -4.161...e-08]]) +>>> retract[-5:,-2:] # doctest: +ELLIPSIS +Data([[ 4.501...e-07, -1.178...e-09], + [ 4.501...e-07, -1.156...e-09], + [ 4.501...e-07, -1.269...e-09], + [ 4.510...e-07, -1.518...e-09], + [ 4.513...e-07, -8.613...e-10]]) +""" -- 2.26.2