1 | # Python tools to transform from U. Wyoming sounding file to netCDF |
---|
2 | # http://weather.uwyo.edu/upperair/sounding.html |
---|
3 | # L. Fita, CIMA August 2017 |
---|
4 | ## |
---|
5 | ## e.g. # UWyoming_snd_nc.py -f snd_CORDOBA.txt |
---|
6 | |
---|
7 | ### ASCII files from U. Wyoming can have more than one sounding per file, but from |
---|
8 | ### the same station. |
---|
9 | ### Script spect to find |
---|
10 | # [num] [Text] Observations at [time]Z [Date] |
---|
11 | # |
---|
12 | #----------------------------------------------------------------------------- |
---|
13 | # PRES HGHT TEMP DWPT RELH MIXR DRCT SKNT THTA THTE THTV |
---|
14 | # hPa m C C % g/kg deg knot K K K |
---|
15 | #----------------------------------------------------------------------------- |
---|
16 | # lines of data |
---|
17 | # Station information and sounding indices |
---|
18 | |
---|
19 | # Station identifier: [Value] |
---|
20 | # Station number: [Value] |
---|
21 | # Observation time: [date]/[time] |
---|
22 | # (...) |
---|
23 | # |
---|
24 | # [num] [Text] Observations at [time]Z [Date] |
---|
25 | # (...) |
---|
26 | |
---|
27 | from optparse import OptionParser |
---|
28 | import numpy as np |
---|
29 | from netCDF4 import Dataset as NetCDFFile |
---|
30 | import os |
---|
31 | import re |
---|
32 | import numpy.ma as ma |
---|
33 | # Importing generic tools file 'nc_var_tools.py' |
---|
34 | import nc_var_tools as ncvar |
---|
35 | # Importing generic tools file 'generic_tools.py' |
---|
36 | import generic_tools as gen |
---|
37 | import subprocess as sub |
---|
38 | import time |
---|
39 | |
---|
40 | main = 'UWyoming_snd_nc.py' |
---|
41 | |
---|
42 | ###### ###### ##### #### ### ## # |
---|
43 | |
---|
44 | # Time reference |
---|
45 | TrefS = '1949-12-01 00:00:00' |
---|
46 | TrefYmdHMS = '19491201000000' |
---|
47 | tunits = 'minutes' |
---|
48 | tfmt = '%y%m%d/%H%M' |
---|
49 | |
---|
50 | # Integer text values (all the rest as float) |
---|
51 | intTXTvals = ['Station number'] |
---|
52 | |
---|
53 | # Text values (all the rest as float) |
---|
54 | txtTXTvals = ['Station identifier', 'Observation time'] |
---|
55 | |
---|
56 | # Float values for global attributes: |
---|
57 | floatTXTvals = ['Station elevation', 'Station longitude', 'Station latitude'] |
---|
58 | |
---|
59 | # Not lower pressure variables |
---|
60 | NOTlowpres = ['DWPT', 'RELH', 'MIXR', 'THTE'] |
---|
61 | |
---|
62 | Lstring = 256 |
---|
63 | |
---|
64 | # Arguments |
---|
65 | ## |
---|
66 | parser = OptionParser() |
---|
67 | parser.add_option("-D", "--Debug", dest="debug", |
---|
68 | help="debug prints", metavar="BOOL") |
---|
69 | parser.add_option("-f", "--snd_file", dest="sndfile", |
---|
70 | help="U. Wyoming sounding (http://weather.uwyo.edu/upperair/sounding.html) file to use", |
---|
71 | metavar="FILE") |
---|
72 | |
---|
73 | (opts, args) = parser.parse_args() |
---|
74 | |
---|
75 | ####### ####### |
---|
76 | ## MAIN |
---|
77 | ####### |
---|
78 | |
---|
79 | # Global attributes for the station |
---|
80 | stngattrk = intTXTvals + txtTXTvals + floatTXTvals |
---|
81 | |
---|
82 | if opts.debug is None: |
---|
83 | print gen.infmsg |
---|
84 | print ' ' + main + ": no debug value provided!!" |
---|
85 | print ' assuming:', False |
---|
86 | debug = False |
---|
87 | else: |
---|
88 | debug = gen.Str_Bool(opts.debug) |
---|
89 | |
---|
90 | if not os.path.isfile(opts.sndfile): |
---|
91 | print gen.errormsg |
---|
92 | print ' ' + main + ": sounding file '" + opts.sndfile + "' does not exist !!" |
---|
93 | quit(-1) |
---|
94 | |
---|
95 | # Reading sounding file |
---|
96 | osnd = open(opts.sndfile, 'r') |
---|
97 | |
---|
98 | # Processed sounding |
---|
99 | soundings = {} |
---|
100 | |
---|
101 | # Processed dates |
---|
102 | idate = 0 |
---|
103 | |
---|
104 | # List with the dates of the soundings, to keep them consecutive!! |
---|
105 | Tsoundings = [] |
---|
106 | |
---|
107 | for line in osnd: |
---|
108 | if line[0:1] != '#' and line[0:1] != '<' and line[0:1] != '-' and len(line) > 1: |
---|
109 | lvals = gen.values_line(line, ' ', ['\t']) |
---|
110 | newsnd = lvals.count('Observations') |
---|
111 | # Processing a new date |
---|
112 | if newsnd != 0: |
---|
113 | if idate == 0: |
---|
114 | print ' reading first sounding!' |
---|
115 | pvals = {} |
---|
116 | txtvals = {} |
---|
117 | idate = idate + 1 |
---|
118 | strefn = lvals[1] |
---|
119 | idobsS = lvals.index('Observations') |
---|
120 | stnameS = ' '.join(lvals[2:idobsS]) |
---|
121 | else: |
---|
122 | stationdateS = str(txtvals['Station number']) + '_' + txtvals['Observation time'] |
---|
123 | print " data for sounding: '" + stationdateS + "' _______" |
---|
124 | |
---|
125 | if (debug): |
---|
126 | for ip in pvals.keys(): |
---|
127 | print ip, ':', pvals[ip] |
---|
128 | for ic in txtvals.keys(): |
---|
129 | print ic, ':', txtvals[ic] |
---|
130 | |
---|
131 | Tsoundings.append(stationdateS) |
---|
132 | soundings[stationdateS] = [pvals, txtvals] |
---|
133 | if len(soundings.keys()) == 1: |
---|
134 | presvals = list(pvals.keys()) |
---|
135 | else: |
---|
136 | noinc = list(set(pvals.keys()).difference(set(presvals))) |
---|
137 | presvals = presvals + noinc |
---|
138 | presvals.sort(reverse=True) |
---|
139 | pvals = {} |
---|
140 | txtvals = {} |
---|
141 | idate = idate + 1 |
---|
142 | elif lvals[0] == 'hPa': |
---|
143 | if (debug): |
---|
144 | print ' getting text values ...' |
---|
145 | if lvals[0] == 'hPa': |
---|
146 | if idate == 1: |
---|
147 | if (debug): |
---|
148 | print ' getting units of the variables' |
---|
149 | varu = {} |
---|
150 | iv = 0 |
---|
151 | for vn in sndvarn: |
---|
152 | varu[vn] = lvals[iv] |
---|
153 | iv = iv + 1 |
---|
154 | elif lvals[0] == '-': |
---|
155 | print ' ' |
---|
156 | # elif lvals[0].index() == |
---|
157 | elif lvals[0] == 'PRES': |
---|
158 | # doing nothing! |
---|
159 | sndvarn = list(lvals) |
---|
160 | else: |
---|
161 | # Numeric values |
---|
162 | if gen.IsNumber(lvals[0], 'R') and len(lvals) > 1 and lvals[1] != 'hPa': |
---|
163 | Sline = line.replace('\n', '').replace('\t', '') |
---|
164 | linvals = gen.getting_fixedline(Sline,[7,14,21,28,35,42,49,56,63,70],\ |
---|
165 | ['R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R']) |
---|
166 | if (debug): |
---|
167 | print ' getting values at pressure:', np.float(lvals[0]) |
---|
168 | pvals[np.float(lvals[0])] = np.array(linvals[1:], dtype=np.float) |
---|
169 | else: |
---|
170 | if (debug): |
---|
171 | print ' getting text values ...' |
---|
172 | if lvals[0] == 'hPa': |
---|
173 | if idate == 1: |
---|
174 | if (debug): |
---|
175 | print ' getting units of the variables' |
---|
176 | varu = {} |
---|
177 | iv = 0 |
---|
178 | for vn in sndvarn: |
---|
179 | varu[vn] = lvals[iv] |
---|
180 | iv = iv + 1 |
---|
181 | else: |
---|
182 | txt = '' |
---|
183 | # Specific work for 'Observation time:' |
---|
184 | if gen.searchInlist(lvals, 'Observation'): |
---|
185 | txtvals['Observation time'] = lvals[2] |
---|
186 | else: |
---|
187 | for iv in range(len(lvals)): |
---|
188 | if not gen.IsNumber(lvals[iv], 'R'): |
---|
189 | if len(txt) == 0: |
---|
190 | txt = lvals[iv] |
---|
191 | else: |
---|
192 | txt = txt + ' ' + lvals[iv] |
---|
193 | else: |
---|
194 | # Specific work for '1000 hPa to 500 hPa thickness:' |
---|
195 | if gen.searchInlist(lvals, 'thickness:') and iv <= 5: |
---|
196 | if len(txt) == 0: |
---|
197 | txt = lvals[iv] |
---|
198 | else: |
---|
199 | txt = txt + ' ' + lvals[iv] |
---|
200 | else: |
---|
201 | txtn = txt.replace(':', '') |
---|
202 | if gen.searchInlist(intTXTvals, txtn): |
---|
203 | txtvals[txtn] = int(lvals[iv]) |
---|
204 | elif gen.searchInlist(txtTXTvals, txtn): |
---|
205 | txtvals[txtn] = lvals[iv] |
---|
206 | else: |
---|
207 | txtvals[txtn] = np.float(lvals[iv]) |
---|
208 | if (debug): |
---|
209 | print " text value: '" + txtn + "':", txtvals[txtn] |
---|
210 | |
---|
211 | # Including last sounding |
---|
212 | stationdateS = str(txtvals['Station number']) + '_' + txtvals['Observation time'] |
---|
213 | print " data for sounding: '" + stationdateS + "' _______" |
---|
214 | |
---|
215 | if (debug): |
---|
216 | for ip in pvals.keys(): |
---|
217 | print ip, ':', pvals[ip] |
---|
218 | for ic in txtvals.keys(): |
---|
219 | print ic, ':', txtvals[ic] |
---|
220 | |
---|
221 | Tsoundings.append(stationdateS) |
---|
222 | soundings[stationdateS] = [pvals, txtvals] |
---|
223 | if len(soundings.keys()) == 1: |
---|
224 | presvals = list(pvals.keys()) |
---|
225 | else: |
---|
226 | noinc = list(set(pvals.keys()).difference(set(presvals))) |
---|
227 | presvals = presvals + noinc |
---|
228 | presvals.sort(reverse=True) |
---|
229 | |
---|
230 | osnd.close() |
---|
231 | varns = list(sndvarn) |
---|
232 | varns.remove('PRES') |
---|
233 | |
---|
234 | # Getting measured values |
---|
235 | Ntimes = len(Tsoundings) |
---|
236 | Npres = len(presvals) |
---|
237 | Nvals = len(varns) |
---|
238 | |
---|
239 | sndvals = np.ones((Ntimes, Npres, Nvals), dtype=np.float)*gen.fillValueF |
---|
240 | for it in range(Ntimes): |
---|
241 | snditS = Tsoundings[it] |
---|
242 | sndvs = soundings[snditS] |
---|
243 | sndv = sndvs[0] |
---|
244 | for ip in range(Npres): |
---|
245 | if sndv.has_key(presvals[ip]): |
---|
246 | sndpv = sndv[presvals[ip]] |
---|
247 | |
---|
248 | if len(sndpv) == Nvals: |
---|
249 | sndvals[it,ip,:] = sndpv |
---|
250 | else: |
---|
251 | ivv = 0 |
---|
252 | if len(sndpv) == Nvals - len(NOTlowpres): |
---|
253 | for iv in range(Nvals): |
---|
254 | if not gen.searchInlist(NOTlowpres, varns[iv]): |
---|
255 | sndvals[it,ip,iv] = sndpv[ivv] |
---|
256 | ivv = ivv + 1 |
---|
257 | elif len(sndpv) == Nvals - len(NOTlowpres) - 2: |
---|
258 | for iv in range(Nvals): |
---|
259 | if not gen.searchInlist(NOTlowpres, varns[iv]) and \ |
---|
260 | not gen.searchInlist(['DRCT', 'SKNT'], varns[iv]): |
---|
261 | sndvals[it,ip,iv] = sndpv[ivv] |
---|
262 | ivv = ivv + 1 |
---|
263 | elif len(sndpv) == 1: |
---|
264 | sndvals[it,ip,ivv] = sndpv[0] |
---|
265 | |
---|
266 | sndvals = ma.masked_equal(sndvals, gen.fillValueF) |
---|
267 | print ' recupered values shape:', sndvals.shape |
---|
268 | if (debug): |
---|
269 | print ' values from file _______' |
---|
270 | print sndvals |
---|
271 | |
---|
272 | # Removing not computed values |
---|
273 | statglobalattr = {} |
---|
274 | txtn = list(txtvals.keys()) |
---|
275 | snditS = Tsoundings[0] |
---|
276 | sndvs = soundings[snditS] |
---|
277 | sndc = sndvs[1] |
---|
278 | |
---|
279 | for Sn in intTXTvals: |
---|
280 | SnS = Sn.replace(' ','_') |
---|
281 | if gen.searchInlist(txtn, Sn): |
---|
282 | if sndc.has_key(Sn): statglobalattr[SnS] = int(sndc[Sn]) |
---|
283 | else: statglobalattr[SnS] = '-' |
---|
284 | txtn.remove(Sn) |
---|
285 | else: |
---|
286 | if sndc.has_key(Sn): statglobalattr[SnS] = int(sndc[Sn]) |
---|
287 | else: statglobalattr[SnS] = '-' |
---|
288 | |
---|
289 | for Sn in txtTXTvals: |
---|
290 | SnS = Sn.replace(' ','_') |
---|
291 | if gen.searchInlist(txtn, Sn): |
---|
292 | if sndc.has_key(Sn): statglobalattr[SnS] = sndc[Sn] |
---|
293 | else: statglobalattr[SnS] = '-' |
---|
294 | txtn.remove(Sn) |
---|
295 | else: |
---|
296 | if sndc.has_key(Sn): statglobalattr[SnS] = int(sndc[Sn]) |
---|
297 | else: statglobalattr[SnS] = '-' |
---|
298 | |
---|
299 | for Sn in floatTXTvals: |
---|
300 | SnS = Sn.replace(' ','_') |
---|
301 | if gen.searchInlist(txtn, Sn): |
---|
302 | if sndc.has_key(Sn): statglobalattr[SnS] = np.float(sndc[Sn]) |
---|
303 | else: statglobalattr[Sn] = '-' |
---|
304 | txtn.remove(Sn) |
---|
305 | else: |
---|
306 | if sndc.has_key(Sn): statglobalattr[SnS] = int(sndc[Sn]) |
---|
307 | else: statglobalattr[SnS] = '-' |
---|
308 | |
---|
309 | Ncomp = len(txtn) |
---|
310 | compvals = np.ones((Ntimes, Ncomp), dtype=np.float)*gen.fillValueF |
---|
311 | |
---|
312 | tvals = [] |
---|
313 | for it in range(Ntimes): |
---|
314 | snditS = Tsoundings[it] |
---|
315 | sndvs = soundings[snditS] |
---|
316 | sndc = sndvs[1] |
---|
317 | tvals.append(sndc['Observation time']) |
---|
318 | for ic in range(len(txtn)): |
---|
319 | if sndc.has_key(txtn[ic]): |
---|
320 | compvals[it,ic] = sndc[txtn[ic]] |
---|
321 | |
---|
322 | # netCDF file creation |
---|
323 | ## |
---|
324 | ofilen = 'UWyoming_snd_' + str(txtvals['Station number']) + '.nc' |
---|
325 | |
---|
326 | onewnc = NetCDFFile(ofilen, 'w') |
---|
327 | |
---|
328 | # Creation of dimensions |
---|
329 | onewnc.createDimension('pres', Npres) |
---|
330 | onewnc.createDimension('time', None) |
---|
331 | #onewnc.createDimension('cvals', Ncomp) |
---|
332 | #onewnc.createDimension('Lstring', Lstring) |
---|
333 | |
---|
334 | # Creation of variable-dimensions |
---|
335 | newvar = onewnc.createVariable('pres', 'f8', ('pres')) |
---|
336 | newvar[:] = presvals |
---|
337 | ncvar.basicvardef(newvar, 'pres', 'pressure', varu['PRES']) |
---|
338 | |
---|
339 | # No more computedvalues matrix ! |
---|
340 | #newvar = onewnc.createVariable('cvals', 'c', ('cvals', 'Lstring')) |
---|
341 | #ncvar.writing_str_nc(newvar, txtn, Lstring) |
---|
342 | #ncvar.basicvardef(newvar, 'cvals', 'computed values from sounding data', 'hPa') |
---|
343 | |
---|
344 | newvar = onewnc.createVariable('time', 'f8', ('time')) |
---|
345 | timeT = [] |
---|
346 | atimeT = np.zeros((len(tvals),6), dtype=int) |
---|
347 | iit = 0 |
---|
348 | for it in tvals: |
---|
349 | dateT = time.strptime(it, tfmt) |
---|
350 | timeT.append(dateT) |
---|
351 | atimeT[iit,:] = np.array([dateT.tm_year, dateT.tm_mon, dateT.tm_mday, \ |
---|
352 | dateT.tm_hour, dateT.tm_min, dateT.tm_sec]) |
---|
353 | iit = iit + 1 |
---|
354 | |
---|
355 | CFtimes = gen.realdatetime_CFcompilant(atimeT, TrefYmdHMS, tunits) |
---|
356 | newvar[:] = CFtimes |
---|
357 | ncvar.basicvardef(newvar, 'time', 'time', tunits + ' since ' + TrefS) |
---|
358 | ncvar.set_attribute(newvar, 'calendar', 'gregorian') |
---|
359 | |
---|
360 | # Filling with sounding values |
---|
361 | iv = 0 |
---|
362 | for varn in varns: |
---|
363 | CFvals = gen.variables_values(varn) |
---|
364 | newvar = onewnc.createVariable(CFvals[0], 'f', ('time', 'pres'), \ |
---|
365 | fill_value=gen.fillValueF) |
---|
366 | newvar[:] = sndvals[:,:,iv] |
---|
367 | ncvar.basicvardef(newvar, varn, CFvals[4].replace('|', ' '), varu[varn]) |
---|
368 | iv = iv + 1 |
---|
369 | onewnc.sync() |
---|
370 | |
---|
371 | # No more computedvalues matrix ! |
---|
372 | #newvar = onewnc.createVariable('computedvals', 'f', ('time', 'cvals'), \ |
---|
373 | # fill_value=gen.fillValueF) |
---|
374 | #newvar[:] = compvals |
---|
375 | #ncvar.basicvardef(newvar, 'computedvals', 'values computed from sounding data', '-') |
---|
376 | #onewnc.sync() |
---|
377 | |
---|
378 | # Getting specific 1D values |
---|
379 | for ic in range(len(txtn)): |
---|
380 | if len(txtn[ic]) < 2: continue |
---|
381 | Sn = txtn[ic] |
---|
382 | CFvalues = gen.variables_values(Sn) |
---|
383 | newvar=onewnc.createVariable(CFvalues[0],'f', ('time'), fill_value=gen.fillValueF) |
---|
384 | newvar[:] = compvals[:,ic] |
---|
385 | ncvar.basicvardef(newvar, CFvalues[1], CFvalues[4].replace('|',' '), CFvalues[5]) |
---|
386 | onewnc.sync() |
---|
387 | |
---|
388 | # Global attributes |
---|
389 | ncvar.add_global_PyNCplot(onewnc, main, '', '0.2') |
---|
390 | onewnc.setncattr('Station_ref', strefn) |
---|
391 | onewnc.setncattr('Station_name', stnameS) |
---|
392 | |
---|
393 | for atn in stngattrk: |
---|
394 | atnS = atn.replace(' ','_') |
---|
395 | onewnc.setncattr(atnS, statglobalattr[atnS]) |
---|
396 | |
---|
397 | onewnc.close() |
---|
398 | print main + ": succesful writing of sounding file '" + ofilen + "' !!" |
---|
399 | |
---|