Use relative imports (PEP 328) for calibcant sibling imports.
[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
37 import data_logger
38 from splittable_kwargs import splittableKwargsFunction, \
39     make_splittable_kwargs_function
40
41 import .common
42 import .config
43
44
45 def C_to_K(celsius) :
46     "Convert Celsius -> Kelvin."
47     return celsius + 273.15
48
49 def K_to_K(kelvin) :
50     "Convert Kelvin -> Kelvin."
51     return kelvin
52
53 @splittableKwargsFunction()
54 def T_analyze(T, convert_to_K=C_to_K) :
55     """
56     Not much to do here, just convert to Kelvin.
57     Uses C_to_K (defined above) by default.
58     """
59     try : # if T is an array, convert element by element
60         for i in range(len(T)) :
61             T[i] = convert_to_K(T[i])
62     except TypeError : # otherwise, make an array from a single T
63         T = [convert_to_K(T)]
64     return T
65
66 @splittableKwargsFunction()
67 def T_save(T, log_dir=None) :
68     """
69     Save either a single T (if you are crazy :p),
70     or an array of them (more reasonable)
71     """
72     T = numpy.array(T, dtype=numpy.float)
73     if log_dir != None :
74         log = data_logger.data_log(log_dir, noclobber_logsubdir=False,
75                                    log_name="T_float")
76         log.write_binary(T.tostring())
77     if config.LOG_DATA != None :
78         log = data_logger.data_log(config.LOG_DIR, noclobber_logsubdir=False,
79                                    log_name="T_float")
80         log.write_binary(T.tostring())
81         
82 def T_load(datafile=None) :
83     """
84     Load the saved T array (possibly of length 1), and return it.  If
85     datafile == None, return an array of one config.DEFAULT_TEMP
86     instead.
87     """
88     if datafile == None :
89         return numpy.array([config.DEFAULT_TEMP], dtype=numpy.float)
90     else :
91         dl = data_logger.data_load()
92         return dl.read_binary(datafile)
93
94 @splittableKwargsFunction()
95 def T_plot(T, plotVerbose=False) :
96     """
97     Umm, just print the temperature?
98     """
99     if plotVerbose or config.PYLAB_VERBOSE or config.TEXT_VERBOSE :
100         print "Temperature ", T
101
102 @splittableKwargsFunction((T_analyze, 'T', 'convert_to_K'),
103                           (T_plot, 'T'))
104 def T_load_analyze_tweaked(tweak_file, convert_to_K=C_to_K, textVerboseFile=None, **kwargs) :
105     "Load all the T array files from a tweak file and return a single array"
106     T_analyze_kwargs,T_plot_kwargs = \
107         T_load_analyze_tweaked._splitargs(T_load_analyze_tweaked, kwargs)
108     Ts = []
109     for line in file(tweak_file, 'r') :
110         parsed = line.split()
111         path = parsed[0].strip()
112         if path[0] == '#': # a comment
113             continue
114         if textVerboseFile != None :
115             print >> textVerboseFile, "Reading data from %s" % (path)
116         # read the data
117         data = T_load(path)
118         Ts.extend(data)
119     T_analyze(Ts, convert_to_K=convert_to_K)
120     return numpy.array(Ts, dtype=numpy.float)
121
122 # commandline interface functions
123 import scipy.io, sys
124
125 def read_data(ifile):
126     "ifile can be a filename string or open (seekable) file object"
127     if ifile == None :  ifile = sys.stdin
128     unlabeled_data=scipy.io.read_array(ifile)
129     return unlabeled_data
130
131 if __name__ == '__main__' :
132     # command line interface
133     from optparse import OptionParser
134     
135     usage_string = ('%prog <input-file>\n'
136                     '2008, W. Trevor King.\n'
137                     '\n'
138                     'There are two operation modes, one to analyze a single T (temperature) file,\n'
139                     'and one to analyze tweak files.\n'
140                     '\n'
141                     'Single file mode (the default) :\n'
142                     'Reads in single column ASCII file of temperatures and... prints them back out.\n'
143                     'No need to do this, but the option is available for consistency with the other\n'
144                     'calibcant modules.\n'
145                     '\n'
146                     'Tweak file mode:\n'
147                     'Runs the same analysis as in single file mode for each T file in\n'
148                     'a tweak file.  Each line in the tweak file specifies a single T file.\n'
149                     'Blank lines and those beginning with a pound sign (#) are ignored.\n'
150                     'A T file contains a sequence of 32 bit floats representing temperature in K.\n'
151                     )
152     parser = OptionParser(usage=usage_string, version='%prog 0.1')
153     parser.add_option('-C', '--celsius', dest='celsius',
154                       help='Use Celsius input temperatures instead of Kelvin (default %default)\n',
155                       action='store_true', default=False)
156     parser.add_option('-o', '--output-file', dest='ofilename',
157                       help='write output to FILE (default stdout)',
158                       type='string', metavar='FILE')
159     parser.add_option('-c', '--comma-out', dest='comma_out', action='store_true',
160                       help='Output comma-seperated values (default %default)',
161                       default=False)
162     parser.add_option('-t', '--tweak-mode', dest='tweakmode', action='store_true',
163                       help='Run in tweak-file mode',
164                       default=False)
165     parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
166                       help='Print lots of debugging information',
167                       default=False)
168
169     options,args = parser.parse_args()
170     parser.destroy()
171     assert len(args) >= 1, "Need an input file"
172         
173     ifilename = args[0]
174
175     if options.ofilename != None :
176         ofile = file(options.ofilename, 'w')
177     else :
178         ofile = sys.stdout
179     if options.verbose == True :
180         vfile = sys.stderr
181     else :
182         vfile = None
183     config.TEXT_VERBOSE = options.verbose
184     config.PYLAB_VERBOSE = False
185     config.GNUPLOT_VERBOSE = False
186     if options.celsius :
187         convert_to_K = C_to_K
188     else :
189         convert_to_K = K_to_K
190     
191     if options.tweakmode == False :
192         data = read_data(ifilename)
193         Ts = T_analyze(data, convert_to_K)
194     else : # tweak file mode
195         Ts = T_load_analyze_tweaked(ifilename, convert_to_K, textVerboseFile=vfile)
196
197     if options.comma_out :
198         sep = ','
199     else :
200         sep = '\n'
201     common.write_array(ofile, Ts, sep)
202     
203     if options.ofilename != None :
204         ofile.close()