Fix relative import syntax.
[calibcant.git] / calibcant / T_analyze.py
1 #!/usr/bin/python
2 #
3 # calibcant - tools for thermally calibrating AFM cantilevers
4 #
5 # Copyright (C) 2008-2010 W. Trevor King <wking@drexel.edu>
6 #
7 # This file is part of CalibCant.
8 #
9 # CalibCant is free software: you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation, either
12 # version 3 of the License, or (at your option) any later version.
13 #
14 # CalibCant is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU Lesser General Public License for more details.
18 #
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with CalibCant.  If not, see
21 # <http://www.gnu.org/licenses/>.
22
23 """
24 Separate the more general T_analyze() from the other T_*()
25 functions in calibcant.  Also provide a command line interface
26 for analyzing data acquired through other workflows.
27
28 The relevant physical quantities are :
29  T Temperature at which thermal vibration measurements were aquired
30 """
31
32 import numpy
33
34 import data_logger
35 from splittable_kwargs import splittableKwargsFunction, \
36     make_splittable_kwargs_function
37
38 from . import common
39 from . import config
40
41
42 def C_to_K(celsius) :
43     "Convert Celsius -> Kelvin."
44     return celsius + 273.15
45
46 def K_to_K(kelvin) :
47     "Convert Kelvin -> Kelvin."
48     return kelvin
49
50 @splittableKwargsFunction()
51 def T_analyze(T, convert_to_K=C_to_K) :
52     """
53     Not much to do here, just convert to Kelvin.
54     Uses C_to_K (defined above) by default.
55     """
56     try : # if T is an array, convert element by element
57         for i in range(len(T)) :
58             T[i] = convert_to_K(T[i])
59     except TypeError : # otherwise, make an array from a single T
60         T = [convert_to_K(T)]
61     return T
62
63 @splittableKwargsFunction()
64 def T_save(T, log_dir=None) :
65     """
66     Save either a single T (if you are crazy :p),
67     or an array of them (more reasonable)
68     """
69     T = numpy.array(T, dtype=numpy.float)
70     if log_dir != None :
71         log = data_logger.data_log(log_dir, noclobber_logsubdir=False,
72                                    log_name="T_float")
73         log.write_binary(T.tostring())
74     if config.LOG_DATA != None :
75         log = data_logger.data_log(config.LOG_DIR, noclobber_logsubdir=False,
76                                    log_name="T_float")
77         log.write_binary(T.tostring())
78         
79 def T_load(datafile=None) :
80     """
81     Load the saved T array (possibly of length 1), and return it.  If
82     datafile == None, return an array of one config.DEFAULT_TEMP
83     instead.
84     """
85     if datafile == None :
86         return numpy.array([config.DEFAULT_TEMP], dtype=numpy.float)
87     else :
88         dl = data_logger.data_load()
89         return dl.read_binary(datafile)
90
91 @splittableKwargsFunction()
92 def T_plot(T, plotVerbose=False) :
93     """
94     Umm, just print the temperature?
95     """
96     if plotVerbose or config.PYLAB_VERBOSE or config.TEXT_VERBOSE :
97         print "Temperature ", T
98
99 @splittableKwargsFunction((T_analyze, 'T', 'convert_to_K'),
100                           (T_plot, 'T'))
101 def T_load_analyze_tweaked(tweak_file, convert_to_K=C_to_K, textVerboseFile=None, **kwargs) :
102     "Load all the T array files from a tweak file and return a single array"
103     T_analyze_kwargs,T_plot_kwargs = \
104         T_load_analyze_tweaked._splitargs(T_load_analyze_tweaked, kwargs)
105     Ts = []
106     for line in file(tweak_file, 'r') :
107         parsed = line.split()
108         path = parsed[0].strip()
109         if path[0] == '#': # a comment
110             continue
111         if textVerboseFile != None :
112             print >> textVerboseFile, "Reading data from %s" % (path)
113         # read the data
114         data = T_load(path)
115         Ts.extend(data)
116     T_analyze(Ts, convert_to_K=convert_to_K)
117     return numpy.array(Ts, dtype=numpy.float)
118
119 # commandline interface functions
120 import scipy.io, sys
121
122 def read_data(ifile):
123     "ifile can be a filename string or open (seekable) file object"
124     if ifile == None :  ifile = sys.stdin
125     unlabeled_data=scipy.io.read_array(ifile)
126     return unlabeled_data
127
128 if __name__ == '__main__' :
129     # command line interface
130     from optparse import OptionParser
131     
132     usage_string = ('%prog <input-file>\n'
133                     '2008, W. Trevor King.\n'
134                     '\n'
135                     'There are two operation modes, one to analyze a single T (temperature) file,\n'
136                     'and one to analyze tweak files.\n'
137                     '\n'
138                     'Single file mode (the default) :\n'
139                     'Reads in single column ASCII file of temperatures and... prints them back out.\n'
140                     'No need to do this, but the option is available for consistency with the other\n'
141                     'calibcant modules.\n'
142                     '\n'
143                     'Tweak file mode:\n'
144                     'Runs the same analysis as in single file mode for each T file in\n'
145                     'a tweak file.  Each line in the tweak file specifies a single T file.\n'
146                     'Blank lines and those beginning with a pound sign (#) are ignored.\n'
147                     'A T file contains a sequence of 32 bit floats representing temperature in K.\n'
148                     )
149     parser = OptionParser(usage=usage_string, version='%prog 0.1')
150     parser.add_option('-C', '--celsius', dest='celsius',
151                       help='Use Celsius input temperatures instead of Kelvin (default %default)\n',
152                       action='store_true', default=False)
153     parser.add_option('-o', '--output-file', dest='ofilename',
154                       help='write output to FILE (default stdout)',
155                       type='string', metavar='FILE')
156     parser.add_option('-c', '--comma-out', dest='comma_out', action='store_true',
157                       help='Output comma-seperated values (default %default)',
158                       default=False)
159     parser.add_option('-t', '--tweak-mode', dest='tweakmode', action='store_true',
160                       help='Run in tweak-file mode',
161                       default=False)
162     parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
163                       help='Print lots of debugging information',
164                       default=False)
165
166     options,args = parser.parse_args()
167     parser.destroy()
168     assert len(args) >= 1, "Need an input file"
169         
170     ifilename = args[0]
171
172     if options.ofilename != None :
173         ofile = file(options.ofilename, 'w')
174     else :
175         ofile = sys.stdout
176     if options.verbose == True :
177         vfile = sys.stderr
178     else :
179         vfile = None
180     config.TEXT_VERBOSE = options.verbose
181     config.PYLAB_VERBOSE = False
182     config.GNUPLOT_VERBOSE = False
183     if options.celsius :
184         convert_to_K = C_to_K
185     else :
186         convert_to_K = K_to_K
187     
188     if options.tweakmode == False :
189         data = read_data(ifilename)
190         Ts = T_analyze(data, convert_to_K)
191     else : # tweak file mode
192         Ts = T_load_analyze_tweaked(ifilename, convert_to_K, textVerboseFile=vfile)
193
194     if options.comma_out :
195         sep = ','
196     else :
197         sep = '\n'
198     common.write_array(ofile, Ts, sep)
199     
200     if options.ofilename != None :
201         ofile.close()