Only calculate relative errors if a previous value exists.
[calibcant.git] / calibcant / T_analyze.py
1 # calibcant - tools for thermally calibrating AFM cantilevers
2 #
3 # Copyright (C) 2008-2012 W. Trevor King <wking@drexel.edu>
4 #
5 # This file is part of calibcant.
6 #
7 # calibcant is free software: you can redistribute it and/or modify it under
8 # the terms of the GNU General Public License as published by the Free Software
9 # Foundation, either version 3 of the License, or (at your option) any later
10 # version.
11 #
12 # calibcant is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # calibcant.  If not, see <http://www.gnu.org/licenses/>.
18
19 """Temperature analysis.
20
21 Separate the more general `T_analyze()` from the other `T_*()`
22 functions in calibcant.
23
24 The relevant physical quantities are:
25
26 * `T` Temperature at which thermal vibration measurements were acquired
27
28 >>> import os
29 >>> import tempfile
30 >>> import numpy
31 >>> from .config import TemperatureConfig
32 >>> from h5config.storage.hdf5 import pprint_HDF5, HDF5_Storage
33
34 >>> fd,filename = tempfile.mkstemp(suffix='.h5', prefix='calibcant-')
35 >>> os.close(fd)
36
37 >>> temperature_config = TemperatureConfig(storage=HDF5_Storage(
38 ...         filename=filename, group='/T/config/'))
39
40 >>> raw_T = numpy.array([22, 23.5, 24])
41 >>> processed_T = T_analyze(raw_T, temperature_config)
42 >>> T_plot(raw_T=raw_T, processed_T=processed_T)
43 >>> T_save(filename=filename, group='/T/', raw_T=raw_T,
44 ...     temperature_config=temperature_config, processed_T=processed_T)
45
46 >>> pprint_HDF5(filename)  # doctest: +REPORT_UDIFF
47 /
48   /T
49     /T/config
50       <HDF5 dataset "default": shape (), type "|b1">
51         True
52       <HDF5 dataset "units": shape (), type "|S7">
53         Celsius
54     <HDF5 dataset "processed": shape (3,), type "<f8">
55       [ 295.15  296.65  297.15]
56     <HDF5 dataset "raw": shape (3,), type "<f8">
57       [ 22.   23.5  24. ]
58
59 >>> raw_T,temperature_config,processed_T = T_load(
60 ...     filename=filename, group='/T/')
61 >>> print temperature_config.dump()
62 units: Celsius
63 default: yes
64 >>> raw_T
65 array([ 22. ,  23.5,  24. ])
66 >>> type(raw_T)
67 <type 'numpy.ndarray'>
68 >>> processed_T
69 array([ 295.15,  296.65,  297.15])
70
71 >>> os.remove(filename)
72 """
73
74 import h5py as _h5py
75 from scipy.constants import C2K as _C2K
76
77 try:
78     import matplotlib as _matplotlib
79     import matplotlib.pyplot as _matplotlib_pyplot
80     import time as _time  # for timestamping lines on plots
81 except (ImportError, RuntimeError), e:
82     _matplotlib = None
83     _matplotlib_import_error = e
84
85 from h5config.storage.hdf5 import HDF5_Storage as _HDF5_Storage
86 from h5config.storage.hdf5 import h5_create_group as _h5_create_group
87
88 from . import LOG as _LOG
89 from . import package_config as _package_config
90 from .config import Celsius as _Celsius
91 from .config import Kelvin as _Kelvin
92 from .config import TemperatureConfig as _TemperatureConfig
93
94
95 def T_analyze(T, temperature_config):
96     """Convert measured temperature to Kelvin.
97
98     `T` should be a numpy ndarray or scalar.  `temperature_config`
99     should be a `config._TemperatureConfig` instance.
100     """
101     if temperature_config['units'] == _Celsius:
102         return _C2K(T)
103     else:
104         return T
105
106 def T_save(filename, group='/', raw_T=None, temperature_config=None,
107            processed_T=None):
108     with _h5py.File(filename, 'a') as f:
109         cwg = _h5_create_group(f, group)
110         if raw_T is not None:
111             try:
112                 del cwg['raw']
113             except KeyError:
114                 pass
115             cwg['raw'] = raw_T
116         if temperature_config is not None:
117             config_cwg = _h5_create_group(cwg, 'config')
118             storage = _HDF5_Storage()
119             storage.save(config=temperature_config, group=config_cwg)
120         if processed_T is not None:
121             try:
122                 del cwg['processed']
123             except KeyError:
124                 pass
125             cwg['processed'] = processed_T
126
127 def T_load(filename, group='/'):
128     assert group.endswith('/')
129     raw_T = processed_T = None
130     with _h5py.File(filename, 'a') as f:
131         try:
132             raw_T = f[group+'raw'][...]
133         except KeyError:
134             pass
135         temperature_config = _TemperatureConfig(storage=_HDF5_Storage(
136                 filename=filename, group=group+'config/'))
137         try:
138             processed_T = f[group+'processed'][...]
139         except KeyError:
140             pass
141     temperature_config.load()
142     return (raw_T, temperature_config, processed_T)
143
144 def T_plot(raw_T=None, processed_T=None):
145     if not _matplotlib:
146         raise _matplotlib_import_error
147     figure = _matplotlib_pyplot.figure()
148     timestamp = _time.strftime('%H%M%S')
149     if raw_T is None:
150         if processed_T is None:
151             return  # nothing to plot
152         axes1 = None
153         axes2 = figure.add_subplot(1, 1, 1)
154     elif processed_T is None:
155         axes1 = figure.add_subplot(1, 1, 1)
156         axes2 = None
157     else:
158         axes1 = figure.add_subplot(2, 1, 1)
159         axes2 = figure.add_subplot(2, 1, 2)
160     if axes1:
161         axes1.set_title('Raw Temperatures %s' % timestamp)
162         axes1.plot(raw_T, label='raw')
163     if axes2:
164         axes2.set_title('Processed Temperatures %s' % timestamp)
165         axes2.plot(processed_T, label='processed')
166     if hasattr(figure, 'show'):
167         figure.show()