Move calibcant.calibrate.move_far_from_surface -> pyafm.afm.AFM.move_away_from_surface.
authorW. Trevor King <wking@drexel.edu>
Thu, 15 Mar 2012 21:56:48 +0000 (17:56 -0400)
committerW. Trevor King <wking@drexel.edu>
Thu, 15 Mar 2012 21:56:48 +0000 (17:56 -0400)
calibcant/calibrate.py [deleted file]
pyafm/afm.py

diff --git a/calibcant/calibrate.py b/calibcant/calibrate.py
deleted file mode 100644 (file)
index e7b0204..0000000
+++ /dev/null
@@ -1,402 +0,0 @@
-# calibcant - tools for thermally calibrating AFM cantilevers
-#
-# Copyright (C) 2008-2012 W. Trevor King <wking@drexel.edu>
-#
-# This file is part of calibcant.
-#
-# calibcant is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# calibcant 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# calibcant.  If not, see <http://www.gnu.org/licenses/>.
-
-"""Acquire and analyze cantilever calibration data.
-
-The relevent physical quantities are:
-
-* Vzp_out  Output z-piezo voltage (what we generate)
-* Vzp      Applied z-piezo voltage (after external ZPGAIN)
-* Zp       The z-piezo position
-* Zcant    The cantilever vertical deflection
-* Vphoto   The photodiode vertical deflection voltage (what we measure)
-* Fcant    The force on the cantilever
-* T        The temperature of the cantilever and surrounding solution
-*          (another thing we measure or guess)
-* k_b      Boltzmann's constant
-
-Which are related by the parameters:
-
-* zpGain           Vzp_out / Vzp
-* zpSensitivity    Zp / Vzp
-* photoSensitivity Vphoto / Zcant
-* k_cant           Fcant / Zcant
-
-Cantilever calibration assumes a pre-calibrated z-piezo
-(zpSensitivity) and a amplifier (zpGain).  In our lab, the z-piezo is
-calibrated by imaging a calibration sample, which has features with
-well defined sizes, and the gain is set with a knob on the Nanoscope.
-
-photoSensitivity is measured by bumping the cantilever against the
-surface, where Zp = Zcant (see bump_acquire() and the bump_analyze
-submodule).
-
-k_cant is measured by watching the cantilever vibrate in free solution
-(see the vib_acquire() and the vib_analyze submodule).  The average
-energy of the cantilever in the vertical direction is given by the
-equipartition theorem.
-
-.. math::  \frac{1}{2} k_b T = \frac{1}{2} k_cant <Zcant**2>
-
-so
-
-.. math::   k_cant = \frac{k_b T}{Zcant**2}
-
-but
-
-.. math::   Zcant = \frac{Vphoto}{photoSensitivity}
-
-so
-
-.. math:: k_cant = \frac{k_b T * photoSensitivty^2}{<Vphoto**2>}
-
-We measured photoSensitivity with the surface bumps.  We can either
-measure T using an external function (see temperature.py), or just
-estimate it (see T_acquire() and the T_analyze submodule).  Guessing
-room temp ~22 deg C is actually fairly reasonable.  Assuming the
-actual fluid temperature is within +/- 5 deg, the error in the spring
-constant k_cant is within 5/273.15 ~= 2%.  A time series of Vphoto
-while we're far from the surface and not changing Vzp_out will give us
-the average variance <Vphoto**2>.
-
-We do all these measurements a few times to estimate statistical
-errors.
-
-The functions are layed out in the families::
-
-  bump_*(), vib_*(), T_*(), and calib_*()
-
-For each family, * can be any of:
-
-* acquire       get real-world data
-* save         store real-world data to disk
-* load         get real-world data from disk
-* analyze      interperate the real-world data.
-* plot         show a nice graphic to convince people we're working :p
-
-A family name without any `_*` extension (e.g. `bump()`), runs
-`*_acquire()`, `*_analyze()`, and `*_save()`.  `*_analyze()` will run
-`*_plot()` if `matplotlib` is set in `calibcant.package_config`.
-"""
-
-from numpy import zeros as _zeros
-from numpy import float as _float
-from time import sleep as _sleep
-
-from . import LOG as _LOG
-
-from .bump import bump as _bump
-from .T import T as _T
-from .vib import vib as _vib
-from .analyze import calib_analyze as _calib_analyze
-from .analyze import calib_save as _calib_save
-
-
-def move_far_from_surface(stepper, distance):
-    """Step back approximately `distance` meters.
-    """
-    steps = int(distance/stepper.step_size)
-    _LOG.info('step back %d steps (~%g m)' % (steps, distance))
-    stepper.step_relative(-steps)
-
-def calib_acquire(afm, calibration_config, filename=None, group='/'):
-    """Acquire data for calibrating a cantilever in one function.
-
-    Inputs:
-      afm                 a pyafm.AFM instance
-      calibration_config  a .config._CalibrationConfig instance
-
-    Outputs (all are arrays of recorded data):
-      bumps measured (V_photodiode / nm_tip) proportionality constant
-      Ts    measured temperature (K)
-      vibs  measured V_photodiode variance (Volts**2) in free solution
-
-    The temperatures are collected after moving far from the surface
-    but before and vibrations are measured to give everything time to
-    settle after the big move.
-    """
-    assert group.endswith('/'), group
-
-    bumps = _zeros((calibration_config['num-bumps'],), dtype=_float)
-    for i in range(calibration_config['num-bumps']):
-        _LOG.info('acquire bump %d of %d' % (i, calibration_config['num-bumps']))
-        bumps[i] = _bump(afm=afm, bump_config=calibration_config['bump'],
-                         filename=filename, group='%sbump/%d/' % (group, i))
-    _LOG.debug('bumps: %s' % bumps)
-
-    move_far_from_surface(
-        afm.stepper, distance=calibration_config['vibration-spacing'])
-
-    Ts = _zeros((calibration_config['num-temperatures'],), dtype=_float)
-    for i in range(calibration_config['num-temperatures']):
-        _LOG.info('acquire T %d of %d'
-                 % (i, calibration_config['num-temperatures']))
-        Ts[i] = _T(
-            get_T=afm.get_temperature,
-            temperature_config=calibration_config['temperature'],
-            filename=filename, group='%stemperature/%d/' % (group, i))
-        _sleep(calibration_config['temperature-sleep'])
-    _LOG.debug('temperatures: %s' % Ts)
-
-    # get vibs
-    vibs = _zeros((calibration_config['num-vibrations'],), dtype=_float)
-    for i in range(calibration_config['num-vibrations']):
-        vibs[i] = _vib(
-            piezo=afm.piezo, vibration_config=calibration_config['vibration'],
-            filename=filename, group='%svibration/%d/' % (group, i))
-    _LOG.debug('vibrations: %s' % vibs)
-
-    return (bumps, Ts, vibs)
-
-def calib(afm, calibration_config, filename=None, group='/'):
-    """Calibrate a cantilever in one function.
-
-    Inputs:
-      (see `calib_acquire()`)
-
-    Outputs:
-      k    cantilever spring constant (in N/m, or equivalently nN/nm)
-      k_s  standard deviation in our estimate of k
-
-    >>> import os
-    >>> from pprint import pprint
-    >>> import tempfile
-    >>> from h5config.storage.hdf5 import pprint_HDF5
-    >>> from pycomedi.device import Device
-    >>> from pycomedi.subdevice import StreamingSubdevice
-    >>> from pycomedi.channel import AnalogChannel, DigitalChannel
-    >>> from pycomedi.constant import AREF, IO_DIRECTION, SUBDEVICE_TYPE, UNIT
-    >>> from pypiezo.afm import AFMPiezo
-    >>> from pypiezo.base import PiezoAxis, InputChannel
-    >>> from pypiezo.config import ChannelConfig, AxisConfig
-    >>> from stepper import Stepper
-    >>> from pyafm.afm import AFM
-    >>> from .config import (CalibrationConfig, BumpConfig,
-    ...     TemperatureConfig, VibrationConfig)
-    >>> from .analyze import calib_load_all
-
-    >>> fd,filename = tempfile.mkstemp(suffix='.h5', prefix='calibcant-')
-    >>> os.close(fd)
-
-    >>> d = Device('/dev/comedi0')
-    >>> d.open()
-
-    Setup an `AFMPiezo` instance.
-
-    >>> s_in = d.find_subdevice_by_type(SUBDEVICE_TYPE.ai,
-    ...     factory=StreamingSubdevice)
-    >>> s_out = d.find_subdevice_by_type(SUBDEVICE_TYPE.ao,
-    ...     factory=StreamingSubdevice)
-
-    >>> axis_channel = s_out.channel(
-    ...     0, factory=AnalogChannel, aref=AREF.ground)
-    >>> input_channel = s_in.channel(0, factory=AnalogChannel, aref=AREF.diff)
-    >>> for chan in [axis_channel, input_channel]:
-    ...     chan.range = chan.find_range(unit=UNIT.volt, min=-10, max=10)
-
-    We set the minimum voltage for the `z` axis to -9 (a volt above
-    the minimum possible voltage) to help with testing
-    `.get_surface_position`.  Without this minimum voltage, small
-    calibration errors could lead to a railed -10 V input for the
-    first few surface approaching steps, which could lead to an
-    `EdgeKink` error instead of a `FlatFit` error.
-
-    >>> axis_config = AxisConfig()
-    >>> axis_config.update(
-    ...     {'gain':20, 'sensitivity':8e-9, 'minimum':-9})
-    >>> axis_channel_config = ChannelConfig()
-    >>> axis_channel_config['name'] = 'z'
-    >>> axis_config['channel'] = axis_channel_config
-    >>> input_channel_config = ChannelConfig()
-    >>> input_channel_config['name'] = 'deflection'
-
-    >>> a = PiezoAxis(config=axis_config, axis_channel=axis_channel)
-    >>> a.setup_config()
-
-    >>> c = InputChannel(config=input_channel_config, channel=input_channel)
-    >>> c.setup_config()
-
-    >>> piezo = AFMPiezo(axes=[a], inputs=[c])
-
-    Setup a `stepper` instance.
-
-    >>> s_d = d.find_subdevice_by_type(SUBDEVICE_TYPE.dio)
-    >>> d_channels = [s_d.channel(i, factory=DigitalChannel)
-    ...             for i in (0, 1, 2, 3)]
-    >>> for chan in d_channels:
-    ...     chan.dio_config(IO_DIRECTION.output)
-
-    >>> def write(value):
-    ...     s_d.dio_bitfield(bits=value, write_mask=2**4-1)
-
-    >>> stepper = Stepper(write=write)
-
-    Setup an `AFM` instance.
-
-    >>> afm = AFM(piezo, stepper)
-
-    Test calibration:
-
-    >>> calibration_config = CalibrationConfig()
-    >>> calibration_config['bump'] = BumpConfig()
-    >>> calibration_config['temperature'] = TemperatureConfig()
-    >>> calibration_config['vibration'] = VibrationConfig()
-    >>> calib(afm, calibration_config, filename=filename, group='/')
-    TODO: replace skipped example data with real-world values
-    >>> pprint_HDF5(filename)  # doctest: +ELLIPSIS, +REPORT_UDIFF
-    /
-      /bump
-        /bump/0
-          /bump/0/config
-            /bump/0/config/bump
-              <HDF5 dataset "far-steps": shape (), type "<i4">
-                200
-              ...
-            /bump/0/config/deflection
-              /bump/0/config/deflection/channel
-                <HDF5 dataset "channel": shape (), type "<i4">
-                  0
-                ...
-            /bump/0/config/z
-              /bump/0/config/z/axis
-                /bump/0/config/z/axis/channel
-                  <HDF5 dataset "channel": shape (), type "<i4">
-                    0
-                  ...
-                <HDF5 dataset "gain": shape (), type "<i4">
-                  20
-                ...
-          <HDF5 dataset "processed": shape (), type "<f8">
-            ...
-          /bump/0/raw
-            <HDF5 dataset "deflection": shape (2048,), type "<u2">
-              [...]
-            <HDF5 dataset "z": shape (2048,), type "<u2">
-              [...]
-        /bump/1
-        ...
-      /calibration
-        /calibration/config
-          /calibration/config/bump
-            <HDF5 dataset "far-steps": shape (), type "<i4">
-              200
-            ...
-          <HDF5 dataset "num-bumps": shape (), type "<i4">
-            10
-          ...
-        /calibration/processed
-          /calibration/processed/spring-constant
-            <HDF5 dataset "data": shape (), type "<f8">
-              ...
-            <HDF5 dataset "standard-deviation": shape (), type "<f8">
-              ...
-            <HDF5 dataset "units": shape (), type "|S3">
-              N/m
-        /calibration/raw
-          /calibration/raw/photodiode-sensitivity
-            <HDF5 dataset "data": shape (10,), type "<f8">
-              [...]
-            <HDF5 dataset "units": shape (), type "|S3">
-              V/m
-          /calibration/raw/temperature
-            <HDF5 dataset "data": shape (10,), type "<f8">
-              [...]
-            <HDF5 dataset "units": shape (), type "|S1">
-              K
-          /calibration/raw/thermal-vibration-variance
-            <HDF5 dataset "data": shape (20,), type "<f8">
-              [...]
-            <HDF5 dataset "units": shape (), type "|S3">
-              V^2
-      /temperature
-        /temperature/0
-          /temperature/0/config
-            <HDF5 dataset "default": shape (), type "|b1">
-              False
-            <HDF5 dataset "units": shape (), type "|S7">
-              Celsius
-          <HDF5 dataset "processed": shape (), type "<f8">
-            295.15
-          <HDF5 dataset "raw": shape (), type "<i4">
-            22
-        /temperature/1
-        ...
-      /vibration
-        /vibration/0
-          /vibration/0/config
-            /vibration/0/config/deflection
-              <HDF5 dataset "channel": shape (), type "<i4">
-                0
-              ...
-            /vibration/0/config/vibration
-              <HDF5 dataset "chunk-size": shape (), type "<i4">
-                2048
-              ...
-          <HDF5 dataset "processed": shape (), type "<f8">
-            ...
-          /vibration/0/raw
-            <HDF5 dataset "deflection": shape (65536,), type "<u2">
-              [...]
-        /vibration/1
-        ...
-        /vibration/19
-          ...
-          /vibration/19/raw
-            <HDF5 dataset "deflection": shape (65536,), type "<u2">
-              [...]
-    >>> everything = calib_load_all(filename, '/')
-    >>> pprint(everything)  # doctest: +ELLIPSIS, +REPORT_UDIFF
-    {'bump_details': [{'bump_config': <BumpConfig ...>,
-                       'deflection_channel_config': <ChannelConfig ...>,
-                       'processed_bump': ...,
-                       'raw_bump': {'deflection': array([...], dtype=uint16),
-                                    'z': array([...], dtype=uint16)},
-                       'z_axis_config': <AxisConfig ...>},
-                      ...],
-     'bumps': array([...]),
-     'calibration_config': <CalibrationConfig ...>,
-     'k': ...,
-     'k_s': ...,
-     'temperature_details': [{'processed_temperature': ...,
-                              'raw_temperature': array(22),
-                              'temperature_config': <TemperatureConfig ...>},
-                             ...],
-     'temperatures': array([...]),
-     'vibration_details': [{'deflection_channel_config': <ChannelConfig ...>,
-                            'processed_vibration': ...,
-                            'raw_vibration': array([...], dtype=uint16),
-                            'vibration_config': <VibrationConfig ...>},
-                           ...],
-     'vibrations': array([...])}
-     
-    Close the Comedi device.
-
-    >>> d.close()
-
-    Cleanup our temporary config file.
-
-    os.remove(filename)
-    """
-    bumps, Ts, vibs = calib_acquire(
-        afm, calibration_config, filename=filename, group=group)
-    # TODO: convert vib units?
-    k,k_s = _calib_analyze(bumps, Ts, vibs)
-    _calib_save(filename, group=group+'calibration/', bumps=bumps,
-                temperatures=Ts, vibrations=vibs,
-                calibration_config=calibration_config, k=k, k_s=k_s)
-    return (k, k_s)
index 2c228e16f8f8ddaaa65db9935c1fabd02d596197..3093a07997cc0d47890cd36b0882aed80c798e7a 100644 (file)
@@ -454,3 +454,12 @@ class AFM (object):
                     cd, target_deflection))
             self.stepper.single_step(1)  # step in
             cd = self.piezo.read_deflection()
+
+    def move_away_from_surface(stepper, distance=None):
+        """Step back approximately `distance` meters.
+        """
+        if distance is None:
+            distance = self.config['far']
+        steps = int(distance/self.stepper.step_size)
+        _LOG.info('step back {} steps (~{} m)' % (steps, distance))
+        self.stepper.step_relative(-steps)