From 7bdd9b942a43a6de25db2f0f362e52a011c25c79 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 11 Nov 2011 03:01:00 -0500 Subject: [PATCH] Add 'scaled column addition' command. Example usage: removing linear drifts with scaled_column_addition --block retract --input_column_1 'surface deflection (m)' --input_column_2 'surface distance (m)' --output_column 'flattened surface deflection (m) --scale_1 1 --scale_2 -1 --scale_2_name 'surface deflection offset|non-contact slope' Also: * Allow `None` as a column name in ColumnAccessCommand._get_column(). * Add 'force zero non-contact slope' option to SurfaceContactCommand. * Convert slopes from surface contact fit from deflection/point to deflection/distance. * Fix the returned deflection offset so it is a float and not a length-one tuple (by removing the trailing comma). --- hooke/plugin/curve.py | 90 +++++++++++++++++++++++++++++++++++++++++- hooke/plugin/vclamp.py | 16 ++++++-- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/hooke/plugin/curve.py b/hooke/plugin/curve.py index 821d333..f6e5892 100644 --- a/hooke/plugin/curve.py +++ b/hooke/plugin/curve.py @@ -210,6 +210,8 @@ class ColumnAccessCommand (BlockCommand): if column_name == None: column_name = self._column_arguments[0].name column_name = params[column_name] + if column_name is None: + return None block = self._block(hooke, params, block_name) columns = block.info['columns'] try: @@ -269,7 +271,7 @@ class CurvePlugin (Builtin): GetCommand(self), InfoCommand(self), BlockInfoCommand(self), DeltaCommand(self), ExportCommand(self), DifferenceCommand(self), DerivativeCommand(self), PowerSpectrumCommand(self), - ClearStackCommand(self)] + ScaledColumnAdditionCommand(self), ClearStackCommand(self)] # Define commands @@ -686,6 +688,92 @@ Otherwise, the chunks are end-to-end, and not overlapping. return params +class ScaledColumnAdditionCommand (ColumnAddingCommand): + """Add one affine transformed column to another: `o=A*i1+B*i2+C`. + """ + def __init__(self, plugin): + super(ScaledColumnAdditionCommand, self).__init__( + name='scaled column addition', + columns=[ + ('input column 1', 'input column (m)', """ +Name of the first column to use as the transform input. +""".strip()), + ('input column 2', None, """ +Name of the second column to use as the transform input. +""".strip()), + ], + new_columns=[ + ('output column', 'output column (m)', """ +Name of the column to use as the transform output. +""".strip()), + ], + arguments=[ + Argument(name='scale 1', type='float', default=None, + help=""" +A float value for the first scale constant. +""".strip()), + Argument(name='scale 1 name', type='string', default=None, + help=""" +The name of the first scale constant in the `.info` dictionary. +""".strip()), + Argument(name='scale 2', type='float', default=None, + help=""" +A float value for the second scale constant. +""".strip()), + Argument(name='scale 2 name', type='string', default=None, + help=""" +The name of the second scale constant in the `.info` dictionary. +""".strip()), + Argument(name='constant', type='float', default=None, + help=""" +A float value for the offset constant. +""".strip()), + Argument(name='constant name', type='string', default=None, + help=""" +The name of the offset constant in the `.info` dictionary. +""".strip()), + ], + help=self.__doc__, plugin=plugin) + + def _run(self, hooke, inqueue, outqueue, params): + self._add_to_command_stack(params) + i1 = self._get_column(hooke=hooke, params=params, + column_name='input column 1') + i2 = self._get_column(hooke=hooke, params=params, + column_name='input column 2') + if i1 is None: + i1 = 0 + if i2 is None: + i2 = 0 + # what if both i1 and i2 are None? + a = self._get_constant(params, i1.info, 'scale 1') + b = self._get_constant(params, i2.info, 'scale 2') + c = self._get_constant(params, i1.info, 'constant') + out = a*i1 + b*i2 + c + i1.tofile('1.dat', sep='\n', format='%r') + i2.tofile('2.dat', sep='\n', format='%r') + out.tofile('3.dat', sep='\n', format='%r') + self._set_column(hooke=hooke, params=params, + column_name='output column', values=out) + + def _get_constant(self, params, info, name): + a = params[name] + pname = params[name + ' name'] + b = None + if pname is not None: + pname_entries = pname.split('|') + b = info + for entry in pname_entries: + b = b[entry] + if a is None and b is None: + return 0 + if a is None: + a = 1 + if b is None: + b = 1 + return a*b + + class ClearStackCommand (CurveCommand): """Empty a curve's command stack. """ diff --git a/hooke/plugin/vclamp.py b/hooke/plugin/vclamp.py index d741173..c6c5734 100644 --- a/hooke/plugin/vclamp.py +++ b/hooke/plugin/vclamp.py @@ -102,7 +102,7 @@ Minimum `fit-contact-slope/guessed-contact-slope` ratio for a "good" fit. """ p = params # convenient alias rNC_ignore = self.info['ignore non-contact before index'] - if self.info['force zero non-contact slope'] == True: + if self.info['force zero non-contact slope'] is True: p = list(p) p.append(0.) # restore the non-contact slope parameter r2 = numpy.round(abs(p[2])) @@ -296,6 +296,11 @@ point (for the `wtk` algorithm). help=""" As an alternative to 'ignore index', ignore after the last peak in the peak list stored in the `.info` dictionary. +""".strip()), + Argument(name='force zero non-contact slope', type='bool', + default=False, count=1, + help=""" +Fix the fitted non-contact slope at zero. """.strip()), Argument(name='distance info name', type='string', default='surface distance offset', @@ -476,7 +481,8 @@ Name (without units) for storing fit parameters in the `.info` dictionary. if reverse == True: # approaching, contact region on the right d_data = d_data[::-1] s = SurfacePositionModel(d_data, info={ - 'force zero non-contact slope':True}, + 'force zero non-contact slope': + params['force zero non-contact slope']}, rescale=True) for argument in self._wtk_fit_check_arguments: s.info[argument.name] = params[argument.name] @@ -494,6 +500,10 @@ Name (without units) for storing fit parameters in the `.info` dictionary. s.info['ignore non-contact before index'] = ignore_index offset,contact_slope,surface_index,non_contact_slope = s.fit( outqueue=outqueue) + deflection_offset = offset + contact_slope*surface_index + delta_pos_per_point = z_data[1] - z_data[0] + contact_slope /= delta_pos_per_point # ddef/point -> ddev/dpos + non_contact_slope /= delta_pos_per_point info = { 'offset': offset, 'contact slope': contact_slope, @@ -501,7 +511,6 @@ Name (without units) for storing fit parameters in the `.info` dictionary. 'non-contact slope': non_contact_slope, 'reversed': reverse, } - deflection_offset = offset + contact_slope*surface_index, if reverse == True: surface_index = len(d_data)-1-surface_index return (numpy.round(surface_index), deflection_offset, info) @@ -535,6 +544,7 @@ Name of the spring constant in the `.info` dictionary. def _run(self, hooke, inqueue, outqueue, params): self._add_to_command_stack(params) params = self._setup_params(hooke=hooke, params=params) + # TODO: call .curve.ScaledColumnAdditionCommand def_data = self._get_column(hooke=hooke, params=params, column_name='deflection column') out = def_data * def_data.info[params['spring constant info name']] -- 2.26.2