Convert calibcant to the new, nestable h5config.
[calibcant.git] / calibcant / bump_analyze.py
index 05ef0b0ab872b617d48805583543e6c4e781f60e..ab745b812db66439353bada260fd8744e72d0bb6 100644 (file)
@@ -46,20 +46,23 @@ measured slope `Vphoto/Vout` is converted to `photo_sensitivity` with
 >>> from pprint import pprint
 >>> import tempfile
 >>> import numpy
->>> from .config import HDF5_BumpConfig
->>> from h5config.hdf5 import pprint_HDF5, HDF5_ChannelConfig, HDF5_AxisConfig
+>>> from h5config.storage.hdf5 import pprint_HDF5
+>>> from pypiezo.config import ChannelConfig, AxisConfig
+>>> from .config import BumpConfig
 
 >>> fd,filename = tempfile.mkstemp(suffix='.h5', prefix='calibcant-')
 >>> os.close(fd)
 
->>> bump_config = HDF5_BumpConfig(filename=filename, group='/bump/config/')
->>> bump_config.save()
->>> z_channel_config = HDF5_ChannelConfig(filename=None)
+>>> bump_config = BumpConfig()
+>>> z_channel_config = ChannelConfig()
+>>> z_channel_config['name'] = 'z'
 >>> z_channel_config['maxdata'] = 200
 >>> z_channel_config['conversion-coefficients'] = (0,1)
 >>> z_channel_config['conversion-origin'] = 0
->>> z_axis_config = HDF5_AxisConfig(filename=None)
->>> deflection_channel_config = HDF5_ChannelConfig(filename=None)
+>>> z_axis_config = AxisConfig()
+>>> z_axis_config['channel'] = z_channel_config
+>>> deflection_channel_config = ChannelConfig()
+>>> deflection_channel_config['name'] = 'deflection'
 >>> deflection_channel_config['maxdata'] = 200
 >>> deflection_channel_config['conversion-coefficients'] = (0,1)
 >>> deflection_channel_config['conversion-origin'] = 0
@@ -70,11 +73,10 @@ measured slope `Vphoto/Vout` is converted to `photo_sensitivity` with
 ...     }
 >>> raw_bump['deflection'][:50] = 50
 >>> processed_bump = bump_analyze(
-...     raw_bump, bump_config, z_channel_config, z_axis_config,
-...     deflection_channel_config)
+...     raw_bump, bump_config, z_axis_config, deflection_channel_config)
 >>> bump_plot(data=raw_bump)  # TODO: convert to V and m
 >>> bump_save(filename=filename, group='/bump/', raw_bump=raw_bump,
-...     z_channel_config=z_channel_config, z_axis_config=z_axis_config,
+...     bump_config=bump_config, z_axis_config=z_axis_config,
 ...     deflection_channel_config=deflection_channel_config,
 ...     processed_bump=processed_bump)
 
@@ -82,80 +84,84 @@ measured slope `Vphoto/Vout` is converted to `photo_sensitivity` with
 /
   /bump
     /bump/config
+      /bump/config/bump
+        <HDF5 dataset "far-steps": shape (), type "<i4">
+          200
+        <HDF5 dataset "model": shape (), type "|S9">
+          quadratic
+        <HDF5 dataset "push-depth": shape (), type "<f8">
+          2e-07
+        <HDF5 dataset "push-speed": shape (), type "<f8">
+          1e-06
+        <HDF5 dataset "samples": shape (), type "<i4">
+          1024
+        <HDF5 dataset "setpoint": shape (), type "<f8">
+          2.0
       /bump/config/deflection
         /bump/config/deflection/channel
-          <HDF5 dataset "channel": shape (), type "|S1">
+          <HDF5 dataset "channel": shape (), type "<i4">
             0
-          <HDF5 dataset "conversion-coefficients": shape (), type "|S4">
-            0, 1
-          <HDF5 dataset "conversion-origin": shape (), type "|S1">
+          <HDF5 dataset "conversion-coefficients": shape (2,), type "<i4">
+            [0 1]
+          <HDF5 dataset "conversion-origin": shape (), type "<i4">
             0
           <HDF5 dataset "device": shape (), type "|S12">
             /dev/comedi0
           <HDF5 dataset "inverse-conversion-coefficients": shape (), type "|S1">
 <BLANKLINE>
-          <HDF5 dataset "inverse-conversion-origin": shape (), type "|S3">
+          <HDF5 dataset "inverse-conversion-origin": shape (), type "<f8">
             1.0
-          <HDF5 dataset "maxdata": shape (), type "|S3">
+          <HDF5 dataset "maxdata": shape (), type "<i4">
             200
-          <HDF5 dataset "range": shape (), type "|S1">
+          <HDF5 dataset "name": shape (), type "|S10">
+            deflection
+          <HDF5 dataset "range": shape (), type "<i4">
             1
-          <HDF5 dataset "subdevice": shape (), type "|S2">
+          <HDF5 dataset "subdevice": shape (), type "<i4">
             -1
-      <HDF5 dataset "far-steps": shape (), type "|S3">
-        200
-      <HDF5 dataset "initial-position": shape (), type "|S6">
-        -5e-08
-      <HDF5 dataset "model": shape (), type "|S9">
-        quadratic
-      <HDF5 dataset "push-depth": shape (), type "|S5">
-        2e-07
-      <HDF5 dataset "push-speed": shape (), type "|S5">
-        1e-06
-      <HDF5 dataset "samples": shape (), type "|S4">
-        1024
-      <HDF5 dataset "setpoint": shape (), type "|S3">
-        2.0
       /bump/config/z
         /bump/config/z/axis
-          <HDF5 dataset "gain": shape (), type "|S3">
+          /bump/config/z/axis/channel
+            <HDF5 dataset "channel": shape (), type "<i4">
+              0
+            <HDF5 dataset "conversion-coefficients": shape (2,), type "<i4">
+              [0 1]
+            <HDF5 dataset "conversion-origin": shape (), type "<i4">
+              0
+            <HDF5 dataset "device": shape (), type "|S12">
+              /dev/comedi0
+            <HDF5 dataset "inverse-conversion-coefficients": shape (), type "|S1">
+<BLANKLINE>
+            <HDF5 dataset "inverse-conversion-origin": shape (), type "<f8">
+              1.0
+            <HDF5 dataset "maxdata": shape (), type "<i4">
+              200
+            <HDF5 dataset "name": shape (), type "|S1">
+              z
+            <HDF5 dataset "range": shape (), type "<i4">
+              1
+            <HDF5 dataset "subdevice": shape (), type "<i4">
+              -1
+          <HDF5 dataset "gain": shape (), type "<f8">
             1.0
           <HDF5 dataset "maximum": shape (), type "|S4">
             None
           <HDF5 dataset "minimum": shape (), type "|S4">
             None
-          <HDF5 dataset "sensitivity": shape (), type "|S3">
-            1.0
-        /bump/config/z/channel
-          <HDF5 dataset "channel": shape (), type "|S1">
-            0
-          <HDF5 dataset "conversion-coefficients": shape (), type "|S4">
-            0, 1
-          <HDF5 dataset "conversion-origin": shape (), type "|S1">
-            0
-          <HDF5 dataset "device": shape (), type "|S12">
-            /dev/comedi0
-          <HDF5 dataset "inverse-conversion-coefficients": shape (), type "|S1">
+          <HDF5 dataset "monitor": shape (), type "|S1">
 <BLANKLINE>
-          <HDF5 dataset "inverse-conversion-origin": shape (), type "|S3">
+          <HDF5 dataset "sensitivity": shape (), type "<f8">
             1.0
-          <HDF5 dataset "maxdata": shape (), type "|S3">
-            200
-          <HDF5 dataset "range": shape (), type "|S1">
-            1
-          <HDF5 dataset "subdevice": shape (), type "|S2">
-            -1
     <HDF5 dataset "processed": shape (), type "<f8">
-      1.00000002115
+      1.00...
     /bump/raw
       <HDF5 dataset "deflection": shape (100,), type "<u2">
         [50 50 ... 50 51 52 ... 97 98 99]
       <HDF5 dataset "z": shape (100,), type "<u2">
         [ 0  1  2  3  ... 97 98 99]
 
->>> (raw_bump,bump_config,z_channel_config,z_axis_config,
-...  deflection_channel_config,processed_bump) = bump_load(
-...     filename=filename, group='/bump/')
+>>> (raw_bump,bump_config,z_axis_config,deflection_channel_config,
+...     processed_bump) = bump_load(filename=filename, group='/bump/')
 
 >>> pprint(raw_bump)  # doctest: +ELLIPSIS
 {'deflection': array([50, 50, ... 51, 52, 53, ..., 97, 98, 99], dtype=uint16),
@@ -178,27 +184,27 @@ except (ImportError, RuntimeError), e:
     _matplotlib = None
     _matplotlib_import_error = e
 
-from h5config.hdf5 import h5_create_group as _h5_create_group
+from h5config.storage.hdf5 import HDF5_Storage as _HDF5_Storage
+from h5config.storage.hdf5 import h5_create_group as _h5_create_group
 from pypiezo.base import convert_bits_to_volts as _convert_bits_to_volts
 from pypiezo.base import convert_bits_to_meters as _convert_bits_to_meters
-from pypiezo.config import HDF5_ChannelConfig as _HDF5_ChannelConfig
-from pypiezo.config import HDF5_AxisConfig as _HDF5_AxisConfig
+from pypiezo.config import ChannelConfig as _ChannelConfig
+from pypiezo.config import AxisConfig as _AxisConfig
 
 from . import LOG as _LOG
 from . import package_config as _package_config
 from .config import Linear as _Linear
 from .config import Quadratic as _Quadratic
-from .config import HDF5_BumpConfig as _HDF5_BumpConfig
+from .config import BumpConfig as _BumpConfig
 
 
-def bump_analyze(data, bump_config, z_channel_config, z_axis_config,
+def bump_analyze(data, bump_config, z_axis_config,
                  deflection_channel_config, plot=False):
     """Return the slope of the bump.
 
     Inputs:
       data              dictionary of data in DAC/ADC bits
       bump_config       `.config._BumpConfig` instance
-      z_channel_config  z `pypiezo.config.ChannelConfig` instance
       z_axis_config     z `pypiezo.config.AxisConfig` instance
       deflection_channel_config
                         deflection `pypiezo.config.ChannelConfig` instance
@@ -209,9 +215,11 @@ def bump_analyze(data, bump_config, z_channel_config, z_axis_config,
     Checks for strong correlation (r-value) and low randomness chance
     (p-value).
     """
-    z = _convert_bits_to_meters(z_channel_config, z_axis_config, data['z'])
+    z = _convert_bits_to_meters(z_axis_config, data['z'])
     deflection = _convert_bits_to_volts(
         deflection_channel_config, data['deflection'])
+    high_voltage_rail = _convert_bits_to_volts(
+        deflection_channel_config, deflection_channel_config['maxdata'])
     if bump_config['model'] == _Linear:
         kwargs = {
             'param_guesser': limited_linear_param_guess,
@@ -224,10 +232,12 @@ def bump_analyze(data, bump_config, z_channel_config, z_axis_config,
             'model': limited_quadratic,
             'sensitivity_from_fit_params': limited_quadratic_sensitivity,
             }
-    photo_sensitivity = bump_fit(z, deflection, plot=plot, **kwargs)
+    photo_sensitivity = bump_fit(
+        z, deflection, high_voltage_rail=high_voltage_rail, plot=plot,
+        **kwargs)
     return photo_sensitivity
 
-def limited_linear(x, params):
+def limited_linear(x, params, high_voltage_rail):
     """
     Model the bump as:
       flat region (off-surface)
@@ -238,9 +248,11 @@ def limited_linear(x, params):
       y_contact (y value for the surface-contact kink)
       slope (dy/dx at the surface-contact kink)
     """
-    high_voltage_rail = 2**16 - 1 # bits
     x_contact,y_contact,slope = params
-    y = slope*(x-x_contact) + y_contact
+    off_surface_mask = x <= x_contact
+    on_surface_mask = x > x_contact
+    y = (off_surface_mask * y_contact +
+         on_surface_mask * (y_contact + slope*(x-x_contact)))
     y = _numpy.clip(y, y_contact, high_voltage_rail)
     return y
 
@@ -277,7 +289,7 @@ def limited_linear_sensitivity(params):
     slope = params[2]
     return slope
 
-def limited_quadratic(x, params):
+def limited_quadratic(x, params, high_voltage_rail):
     """
     Model the bump as:
       flat region (off-surface)
@@ -289,9 +301,12 @@ def limited_quadratic(x, params):
       slope (dy/dx at the surface-contact kink)
       quad (d**2 y / dx**2, allow decreasing sensitivity with increased x)
     """
-    high_voltage_rail = 2**16 - 1 # bits
     x_contact,y_contact,slope,quad = params
-    y = slope*(x-x_contact) + quad*(x-x_contact)**2+ y_contact
+    off_surface_mask = x <= x_contact
+    on_surface_mask = x > x_contact
+    y = (off_surface_mask * y_contact +
+         on_surface_mask * (
+            y_contact + slope*(x-x_contact) + quad*(x-x_contact)**2))
     y = _numpy.clip(y, y_contact, high_voltage_rail)
     return y
 
@@ -321,7 +336,7 @@ def limited_quadratic_sensitivity(params):
     slope = params[2]
     return slope
 
-def bump_fit(z, deflection,
+def bump_fit(z, deflection, high_voltage_rail,
              param_guesser=limited_quadratic_param_guess,
              model=limited_quadratic,
              sensitivity_from_fit_params=limited_quadratic_sensitivity,
@@ -340,7 +355,7 @@ def bump_fit(z, deflection,
     """
     _LOG.debug('fit bump data with model %s' % model)
     def residual(p, deflection, z):
-        return model(z, p) - deflection
+        return model(z, p, high_voltage_rail=high_voltage_rail) - deflection
     param_guess = param_guesser(z, deflection)
     p,cov,info,mesg,ier = _leastsq(
         residual, param_guess, args=(deflection, z), full_output=True,
@@ -353,15 +368,15 @@ def bump_fit(z, deflection,
         _LOG.debug('solution converged')
     else:
         _LOG.debug('solution did not converge')
-    if plot or _package_config['matplotlib']:
-        yguess = model(z, param_guess)
-        yfit = model(z, p)
+    if plot or _package_config['matplotlib'] or True:
+        yguess = model(z, param_guess, high_voltage_rail=high_voltage_rail)
+        yfit = model(z, p, high_voltage_rail=high_voltage_rail)
         bump_plot({'z': z, 'deflection': deflection}, yguess=yguess, yfit=yfit)
     return sensitivity_from_fit_params(p)
 
 def bump_save(filename, group='/', raw_bump=None, bump_config=None,
-              z_channel_config=None, z_axis_config=None,
-              deflection_channel_config=None, processed_bump=None):
+              z_axis_config=None, deflection_channel_config=None,
+              processed_bump=None):
     with _h5py.File(filename, 'a') as f:
         cwg = _h5_create_group(f, group)
         if raw_bump is not None:
@@ -372,15 +387,15 @@ def bump_save(filename, group='/', raw_bump=None, bump_config=None,
                     pass
             cwg['raw/z'] = raw_bump['z']
             cwg['raw/deflection'] = raw_bump['deflection']
+        storage = _HDF5_Storage()
         for config,key in [(bump_config, 'config/bump'),
-                           (z_channel_config, 'config/z/channel'),
                            (z_axis_config, 'config/z/axis'),
                            (deflection_channel_config,
                             'config/deflection/channel')]:
             if config is None:
                 continue
             config_cwg = _h5_create_group(cwg, key)
-            config.save(group=config_cwg)
+            storage.save(config=config, group=config_cwg)
         if processed_bump is not None:
             try:
                 del cwg['processed']
@@ -400,11 +415,11 @@ def bump_load(filename, group='/'):
                 }
         except KeyError:
             pass
-        for Config,key in [(_HDF5_BumpConfig, 'config/bump'),
-                           (_HDF5_ChannelConfig, 'config/z/channel'),
-                           (_HDF5_AxisConfig, 'config/z/axis'),
-                           (_HDF5_ChannelConfig, 'config/deflection/channel')]:
-            config = Config(filename=filename, group=group+key)
+        for Config,key in [(_BumpConfig, 'config/bump'),
+                           (_AxisConfig, 'config/z/axis'),
+                           (_ChannelConfig, 'config/deflection/channel')]:
+            config = Config(storage=_HDF5_Storage(
+                    filename=filename, group=group+key))
             configs.append(config)
         try:
             processed_bump = f[group+'processed'][...]