-#!/usr/bin/python
-#
# calibcant - tools for thermally calibrating AFM cantilevers
#
-# Copyright (C) 2007,2008, William Trevor King
+# Copyright (C) 2008-2011 W. Trevor King <wking@drexel.edu>
#
-# This program 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.
+# This file is part of calibcant.
#
-# This program 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.
+# calibcant 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.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-# 02111-1307, USA.
+# 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 Lesser General Public License for more details.
#
-# The author may be contacted at <wking@drexel.edu> on the Internet, or
-# write to Trevor King, Drexel University, Physics Dept., 3141 Chestnut St.,
-# Philadelphia PA 19104, USA.
+# You should have received a copy of the GNU Lesser General Public
+# License along with calibcant. If not, see
+# <http://www.gnu.org/licenses/>.
-"""
-Separate the more general T_analyze() from the other T_*()
-functions in calibcant. Also provide a command line interface
-for analyzing data acquired through other workflows.
+"""Temperature analysis.
+
+Separate the more general `T_analyze()` from the other `T_*()`
+functions in calibcant.
+
+The relevant physical quantities are:
+
+* `T` Temperature at which thermal vibration measurements were acquired
+
+>>> import os
+>>> import tempfile
+>>> import numpy
+>>> from .config import HDF5_TemperatureConfig
+>>> from pypiezo.config import pprint_HDF5
-The relevant physical quantities are :
- T Temperature at which thermal vibration measurements were aquired
+>>> fd,filename = tempfile.mkstemp(suffix='.h5', prefix='calibcant-')
+>>> os.close(fd)
+
+>>> temperature_config = HDF5_TemperatureConfig(
+... filename=filename, group='/T/config/')
+
+>>> raw_T = numpy.array([22, 23.5, 24])
+>>> processed_T = T_analyze(raw_T, temperature_config)
+>>> T_plot(raw_T=raw_T, processed_T=processed_T)
+>>> T_save(filename=filename, group='/T/', raw_T=raw_T,
+... temperature_config=temperature_config, processed_T=processed_T)
+
+>>> pprint_HDF5(filename) # doctest: +REPORT_UDIFF
+/
+ /T
+ /T/config
+ <HDF5 dataset "default": shape (), type "|S3">
+ yes
+ <HDF5 dataset "units": shape (), type "|S7">
+ Celsius
+ <HDF5 dataset "processed": shape (3,), type "<f8">
+ [ 295.15 296.65 297.15]
+ <HDF5 dataset "raw": shape (3,), type "<f8">
+ [ 22. 23.5 24. ]
+
+>>> raw_T,temperature_config,processed_T = T_load(
+... filename=filename, group='/T/')
+>>> print temperature_config.dump()
+units: Celsius
+default: yes
+>>> raw_T
+array([ 22. , 23.5, 24. ])
+>>> type(raw_T)
+<type 'numpy.ndarray'>
+>>> processed_T
+array([ 295.15, 296.65, 297.15])
+
+>>> os.remove(filename)
"""
-import numpy
-import common # common module for the calibcant package
-import config # config module for the calibcant package
-import data_logger
-import linfit
-from splittable_kwargs import splittableKwargsFunction, \
- make_splittable_kwargs_function
+import h5py as _h5py
+from scipy.constants import C2K as _C2K
-def C_to_K(celsius) :
- "Convert Celsius -> Kelvin."
- return celsius + 273.15
+try:
+ import matplotlib as _matplotlib
+ import matplotlib.pyplot as _matplotlib_pyplot
+ import time as _time # for timestamping lines on plots
+except (ImportError, RuntimeError), e:
+ _matplotlib = None
+ _matplotlib_import_error = e
-def K_to_K(kelvin) :
- "Convert Kelvin -> Kelvin."
- return kelvin
+from pypiezo.config import h5_create_group as _h5_create_group
-@splittableKwargsFunction()
-def T_analyze(T, convert_to_K=C_to_K) :
- """
- Not much to do here, just convert to Kelvin.
- Uses C_to_K (defined above) by default.
- """
- try : # if T is an array, convert element by element
- for i in range(len(T)) :
- T[i] = convert_to_K(T[i])
- except TypeError : # otherwise, make an array from a single T
- T = [convert_to_K(T)]
- return T
-
-@splittableKwargsFunction()
-def T_save(T, log_dir=None) :
- """
- Save either a single T (if you are crazy :p),
- or an array of them (more reasonable)
- """
- T = numpy.array(T, dtype=numpy.float)
- if log_dir != None :
- log = data_logger.data_log(log_dir, noclobber_logsubdir=False,
- log_name="T_float")
- log.write_binary(T.tostring())
- if config.LOG_DATA != None :
- log = data_logger.data_log(config.LOG_DIR, noclobber_logsubdir=False,
- log_name="T_float")
- log.write_binary(T.tostring())
-
-def T_load(datafile=None) :
- """
- Load the saved T array (possibly of length 1), and return it. If
- datafile == None, return an array of one config.DEFAULT_TEMP
- instead.
- """
- if datafile == None :
- return numpy.array([config.DEFAULT_TEMP], dtype=numpy.float)
- else :
- dl = data_logger.data_load()
- return dl.read_binary(datafile)
-
-@splittableKwargsFunction()
-def T_plot(T, plotVerbose=False) :
- """
- Umm, just print the temperature?
+from . import LOG as _LOG
+from . import base_config as _base_config
+from .config import Celsius as _Celsius
+from .config import Kelvin as _Kelvin
+from .config import HDF5_TemperatureConfig as _HDF5_TemperatureConfig
+
+
+def T_analyze(T, temperature_config):
+ """Convert measured temperature to Kelvin.
+
+ `T` should be a numpy ndarray or scalar. `temperature_config`
+ should be a `config._TemperatureConfig` instance.
"""
- if plotVerbose or config.PYLAB_VERBOSE or config.TEXT_VERBOSE :
- print "Temperature ", T
-
-@splittableKwargsFunction((T_analyze, 'T', 'convert_to_K'),
- (T_plot, 'T'))
-def T_load_analyze_tweaked(tweak_file, convert_to_K=C_to_K, textVerboseFile=None, **kwargs) :
- "Load all the T array files from a tweak file and return a single array"
- T_analyze_kwargs,T_plot_kwargs = \
- T_load_analyze_tweaked._splitargs(T_load_analyze_tweaked, kwargs)
- Ts = []
- for line in file(tweak_file, 'r') :
- parsed = line.split()
- path = parsed[0].split('\n')[0]
- if textVerboseFile != None :
- print >> textVerboseFile, "Reading data from %s" % (path)
- # read the data
- data = T_load(path)
- Ts.extend(data)
- T_analyze(Ts, convert_to_K=convert_to_K)
- return numpy.array(Ts, dtype=numpy.float)
-
-# commandline interface functions
-import scipy.io, sys
-
-def read_data(ifile):
- "ifile can be a filename string or open (seekable) file object"
- if ifile == None : ifile = sys.stdin
- unlabeled_data=scipy.io.read_array(ifile)
- return unlabeled_data
-
-if __name__ == '__main__' :
- # command line interface
- from optparse import OptionParser
-
- usage_string = ('%prog <input-file>\n'
- '2008, W. Trevor King.\n'
- '\n'
- 'There are two operation modes, one to analyze a single T (temperature) file,\n'
- 'and one to analyze tweak files.\n'
- '\n'
- 'Single file mode (the default) :\n'
- 'Reads in single column ASCII file of temperatures and... prints them back out.\n'
- 'No need to do this, but the option is available for consistency with the other\n'
- 'calibcant modules.\n'
- '\n'
- 'Tweak file mode:\n'
- 'Runs the same analysis as in single file mode for each T file in\n'
- 'a tweak file. Each line in the tweak file specifies a single T file.\n'
- 'A T file contains a sequence of 32 bit floats representing temperature in K.\n'
- )
- parser = OptionParser(usage=usage_string, version='%prog 0.1')
- parser.add_option('-C', '--celsius', dest='celsius',
- help='Use Celsius input temperatures instead of Kelvin (default %default)\n',
- action='store_true', default=False)
- parser.add_option('-o', '--output-file', dest='ofilename',
- help='write output to FILE (default stdout)',
- type='string', metavar='FILE')
- parser.add_option('-c', '--comma-out', dest='comma_out', action='store_true',
- help='Output comma-seperated values (default %default)',
- default=False)
- parser.add_option('-t', '--tweak-mode', dest='tweakmode', action='store_true',
- help='Run in tweak-file mode',
- default=False)
- parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
- help='Print lots of debugging information',
- default=False)
-
- options,args = parser.parse_args()
- parser.destroy()
- assert len(args) >= 1, "Need an input file"
-
- ifilename = args[0]
-
- if options.ofilename != None :
- ofile = file(options.ofilename, 'w')
- else :
- ofile = sys.stdout
- if options.verbose == True :
- vfile = sys.stderr
- else :
- vfile = None
- config.TEXT_VERBOSE = options.verbose
- config.PYLAB_VERBOSE = False
- config.GNUPLOT_VERBOSE = False
- if options.celsius :
- convert_to_K = C_to_K
- else :
- convert_to_K = K_to_K
-
- if options.tweakmode == False :
- data = read_data(ifilename)
- Ts = T_analyze(data, convert_to_K)
- else : # tweak file mode
- Ts = T_load_analyze_tweaked(ifilename, convert_to_K, textVerboseFile=vfile)
-
- if options.comma_out :
- sep = ','
- else :
- sep = '\n'
- common.write_array(ofile, Ts, sep)
-
- if options.ofilename != None :
- ofile.close()
+ if temperature_config['units'] == _Celsius:
+ return _C2K(T)
+ else:
+ return T
+
+def T_save(filename, group='/', raw_T=None, temperature_config=None,
+ processed_T=None):
+ with _h5py.File(filename, 'a') as f:
+ cwg = _h5_create_group(f, group)
+ if raw_T is not None:
+ try:
+ del cwg['raw']
+ except KeyError:
+ pass
+ cwg['raw'] = raw_T
+ if temperature_config:
+ config_cwg = _h5_create_group(cwg, 'config')
+ temperature_config.save(group=config_cwg)
+ if processed_T is not None:
+ try:
+ del cwg['processed']
+ except KeyError:
+ pass
+ cwg['processed'] = processed_T
+
+def T_load(filename, group='/'):
+ assert group.endswith('/')
+ raw_T = processed_T = None
+ with _h5py.File(filename, 'a') as f:
+ try:
+ raw_T = f[group+'raw'][...]
+ except KeyError:
+ pass
+ temperature_config = _HDF5_TemperatureConfig(
+ filename=filename, group=group+'config/')
+ try:
+ processed_T = f[group+'processed'][...]
+ except KeyError:
+ pass
+ temperature_config.load()
+ return (raw_T, temperature_config, processed_T)
+
+def T_plot(raw_T=None, processed_T=None):
+ if not _matplotlib:
+ raise _matplotlib_import_error
+ figure = _matplotlib_pyplot.figure()
+ timestamp = _time.strftime('%H%M%S')
+ if raw_T is None:
+ if processed_T is None:
+ return # nothing to plot
+ axes1 = None
+ axes2 = figure.add_subplot(1, 1, 1)
+ elif processed_T is None:
+ axes1 = figure.add_subplot(1, 1, 1)
+ axes2 = None
+ else:
+ axes1 = figure.add_subplot(2, 1, 1)
+ axes2 = figure.add_subplot(2, 1, 2)
+ if axes1:
+ axes1.set_title('Raw Temperatures %s' % timestamp)
+ axes1.plot(raw_T, label='raw')
+ if axes2:
+ axes2.set_title('Processed Temperatures %s' % timestamp)
+ axes2.plot(processed_T, label='processed')
+ figure.show()