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