NCCOOS Trac Projects: Top | Web | Platforms | Processing | Viz | Sprints | Sandbox | (Wind)

root/raw2proc/trunk/raw2proc/proc_codar_totals.py

Revision 510 (checked in by haines, 10 years ago)

Update Billy Mitchell processing.

Line 
1 #!/usr/bin/env python
2 # Last modified:  Time-stamp: <2013-04-30 16:27:48 haines>
3 """
4 how to parse data, and assert what data and info goes into
5 creating and updating monthly netcdf files
6
7 CODAR SeaSonde Total Sea Surface Currents (LLUV TOT4)
8
9 parser : sample date and time from header (%TimeStamp:)
10          table time version (%TableType:)
11 creator : lat, lon, z, time, u(time, lat, lon), v(time, lat, lon),
12 updater : time, u(time, lat, lon), v(time, lat, lon),
13
14 Check that grid that totals are calculated over has not changed.
15 (%Origin, %GridAxis, %GridAxisType, %GridSpacing all the same)
16
17 Examples
18 --------
19
20 >> (parse, create, update) = load_processors(module_name_without_dot_py)
21 For example,
22 >> (parse, create, update) = load_processors('proc_rdi_logdata_adcp')
23 or
24 >> si = get_config(cn+'.sensor_info')
25 >> (parse, create, update) = load_processors(si['adcp']['proc_module'])
26
27 Then use the generic name of processor to parse data, create or update
28 monthly output file
29
30 >> lines = load_data(filename)
31 >> data = parse(platform_info, sensor_info, lines)
32 >> create(platform_info, sensor_info, data)
33 or
34 >> update(platform_info, sensor_info, data)
35
36 """
37
38 from raw2proc import *
39 from procutil import *
40 from ncutil import *
41
42 now_dt = datetime.utcnow()
43 now_dt.replace(microsecond=0)
44
45 def parser(platform_info, sensor_info, lines):
46     """
47     parse and assign data to variables from CODAR Totals LLUV format
48
49     Notes
50     -----
51     1. Requires grid definition obtained from sensor_info
52     For best coverage of totals, this includes overlapping foot print of HATY, DUCK, LISL and CEDR
53     
54     """
55
56     import numpy
57     from datetime import datetime
58     from time import strptime
59     from StringIO import StringIO
60     from matplotlib.mlab import griddata
61
62     # define the lat/lon grid based on 6km resolution
63     minlat, maxlat = platform_info['lat'] # (34.5, 38)
64     minlon, maxlon = platform_info['lon'] # (-76, -73.)
65     nlat = platform_info['nlat']
66     nlon = platform_info['nlon']
67     yi = numpy.linspace(minlat, maxlat, nlat)
68     xi = numpy.linspace(minlon, maxlon, nlon)
69     xmesh, ymesh = numpy.meshgrid(xi, yi)
70
71     data = {
72         'dt' : numpy.array(numpy.ones((1,), dtype=object)*numpy.nan),
73         'time' : numpy.array(numpy.ones((1,), dtype=long)*numpy.nan),
74         'lon' : numpy.array(numpy.ones((nlon,), dtype=float)*numpy.nan),
75         'lat' : numpy.array(numpy.ones((nlat,), dtype=float)*numpy.nan),
76         'u' : numpy.array(numpy.ones((1,nlon,nlat), dtype=float)*numpy.nan),
77         'v' : numpy.array(numpy.ones((1,nlon,nlat), dtype=float)*numpy.nan),
78         }
79
80     sample_dt, ftype, lluvspec, ncol, nrow = (None, None, None, None, None)
81     # read header that match '%(k): (v)\n' pairs on each line
82     m = re.findall(r'^(%.*):\s*(.*)$', ''.join(lines), re.MULTILINE)
83     for k,v in m:
84         if k == '%TimeStamp':
85             sample_dt = scanf_datetime(v, fmt='%Y %m %d %H %M %S')           
86         elif k == '%TableType':
87             ftype = v
88         elif k == '%LLUVSpec':
89             lluvspec = float(re.split('\s+', v)[0])
90         elif k == '%TableColumns':
91             ncol = int(v)
92         elif k == '%TableRows':
93             nrow = int(v)
94         elif k == '%TableEnd':
95             break
96             # LLUVSpec 1.17 and greater has two tables bracketed by TableStart and TableEnd
97             # get out of this search loop after the first table
98
99     if nrow>2:
100         # read data from string of lines but make it behave like a file object with StringIO
101         s = StringIO(''.join(lines))
102         s.seek(0) # ensures start posn of file
103         d = numpy.loadtxt(s, comments='%')
104         # lat, lon, u, v = numpy.loadtxt(s, usecols=(0,1,2,3), comments='%', unpack=True)
105         
106         if 'TOT4' in ftype:
107             lon = d[:,0]
108             lat = d[:,1]
109             wu = d[:,2]
110             wv = d[:,3]
111             gridflag = d[:,4]
112             wu_std_qual = d[:,5]
113             wv_std_qual = d[:,6]
114             cov_qual = d[:,7]
115             x_dist = d[:,8]
116             y_dist = d[:,9]
117             rang = d[:,10]
118             bearing = d[:,11]
119             vel_mag = d[:,12]
120             vel_dir = d[:,13]
121
122         # ibad = (wu_std_qual==999.) | (wv_std_qual==999.) | (cov_qual==999.)
123         # wu[ibad] = numpy.nan
124         # wv[ibad] = numpy.nan
125
126         # SMH -- April 26, 2013 -- commenting out these columns for now until figure out how to handle
127         # new dynamic form of LLUVSpec 1.17 in TOT4 format, prior versions were static with 6 fields
128         # will have to use %LLUVSpec header info and second table at bottom of file to get dynamic ncols
129             # s1 = d[:,14]
130             # s2 = d[:,15]
131             # s3 = d[:,16]
132             # s4 = d[:,17]
133             # s5 = d[:,18]
134             # s6 = d[:,19]
135
136             try:
137                 uim = griddata(lon, lat, wu, xmesh, ymesh)
138                 vim = griddata(lon, lat, wv, xmesh, ymesh)
139                 # returned masked array as an ndarray with masked values filled with fill_value
140                 ui = uim.filled(fill_value=numpy.nan)
141                 vi = vim.filled(fill_value=numpy.nan)
142                 # print ui.shape
143             except IndexError:
144                 print "raw2proc:  IndexError in griddata() -- skipping data"
145
146     # ---------------------------------------------------------------
147     i = 0
148     data['dt'][i] =  sample_dt #
149     data['time'][i] =  dt2es(sample_dt) #
150     data['lon'] = xi # new longitude grid
151     data['lat'] = yi # new latitude grid
152
153     if nrow and nrow>2:
154         # use transpose so order is (time, x, y) for netcdf convention
155         data['u'][i] = ui.T # u-component of water velocity (cm/s)
156         data['v'][i] = vi.T # v-component of water velocity
157
158     return data
159
160 def creator(platform_info, sensor_info, data):
161     #
162     #
163     title_str = sensor_info['description']+' at '+ platform_info['location']
164     global_atts = {
165         'title' : title_str,
166         'institution' : 'University of North Carolina at Chapel Hill (UNC-CH)',
167         'institution_url' : 'http://nccoos.unc.edu',
168         'institution_dods_url' : 'http://nccoos.unc.edu',
169         'metadata_url' : 'http://nccoos.unc.edu',
170         'references' : 'http://nccoos.unc.edu',
171         'contact' : 'Sara Haines (haines@email.unc.edu)',
172         #
173         'source' : 'surface current observation',
174         'history' : 'raw2proc using ' + sensor_info['process_module'],
175         'comment' : 'File created using pycdf'+pycdfVersion()+' and numpy '+pycdfArrayPkg(),
176         # conventions
177         'Conventions' : 'CF-1.0; SEACOOS-CDL-v2.0',
178         # SEACOOS CDL codes
179         'format_category_code' : 'fixed-map',
180         'institution_code' : platform_info['institution'],
181         'platform_code' : platform_info['id'],
182         'package_code' : sensor_info['id'],
183         # institution specific
184         'project' : 'North Carolina Coastal Ocean Observing System (NCCOOS)',
185         'project_url' : 'http://nccoos.org',
186         # timeframe of data contained in file yyyy-mm-dd HH:MM:SS
187         'start_date' : data['dt'][0].strftime("%Y-%m-%d %H:%M:%S"),
188         'end_date' : data['dt'][-1].strftime("%Y-%m-%d %H:%M:%S"),
189         'release_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
190         #
191         'creation_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
192         'modification_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
193         'process_level' : 'level1',
194         #
195         # must type match to data (e.g. fillvalue is real if data is real)
196         '_FillValue' : numpy.nan,
197         }
198
199     var_atts = {
200         # coordinate variables
201         'time' : {'short_name': 'time',
202                   'long_name': 'Time',
203                   'standard_name': 'time',
204                   'units': 'seconds since 1970-1-1 00:00:00 -0', # UTC
205                   'axis': 'T',
206                   },
207         'lat' : {'short_name': 'lat',
208              'long_name': 'Latitude',
209              'standard_name': 'latitude',
210              'reference':'geographic coordinates',
211              'units': 'degrees_north',
212              'valid_range':(-90.,90.),
213              'axis': 'Y',
214              },
215         'lon' : {'short_name': 'lon',
216                  'long_name': 'Longitude',
217                  'standard_name': 'longitude',
218                  'reference':'geographic coordinates',
219                  'units': 'degrees_east',
220                  'valid_range':(-180.,180.),
221                  'axis': 'Y',
222                  },
223         'z' : {'short_name': 'z',
224                'long_name': 'Height',
225                'standard_name': 'height',
226                'reference':'zero at sea-surface',
227                'units': 'm',
228                'axis': 'Z',
229                },
230         # data variables
231         'u' : {'short_name': 'u',
232                'long_name': 'E/W component of current',
233                'standard_name': 'eastward_current',
234                'units': 'cm sec-1',
235                'reference' : 'clockwise from True East',
236                 },
237         'v' : {'short_name': 'v',
238                'long_name': 'N/S component of current',
239                'standard_name': 'northward_current',
240                'units': 'cm sec-1',
241                'reference' : 'clockwise from True North',
242                 },
243         }
244
245    
246     # dimension names use tuple so order of initialization is maintained
247     dim_inits = (
248         ('ntime', NC.UNLIMITED),
249         ('nlat', platform_info['nlat']),
250         ('nlon', platform_info['nlon']),
251         ('nz', 1),
252         )
253    
254     # using tuple of tuples so order of initialization is maintained
255     # using dict for attributes order of init not important
256     # use dimension names not values
257     # (varName, varType, (dimName1, [dimName2], ...))
258     var_inits = (
259         # coordinate variables
260         ('time', NC.INT, ('ntime',)),
261         ('lat', NC.FLOAT, ('nlat',)),
262         ('lon', NC.FLOAT, ('nlon',)),
263         ('z',  NC.FLOAT, ('nz',)),
264         # data variables
265         ('u', NC.FLOAT, ('ntime','nlon','nlat')),
266         ('v', NC.FLOAT, ('ntime','nlon','nlat')),
267         )
268    
269     # subset data only to month being processed (see raw2proc.process())
270     i = data['in']
271
272     # var data
273     var_data = (
274         ('lat', data['lat']),
275         ('lon', data['lon']),
276         ('z', 0.),
277         #
278         ('time', data['time'][i]),
279         ('u', data['u'][i]),
280         ('v', data['v'][i]),
281         )
282
283     return (global_atts, var_atts, dim_inits, var_inits, var_data)
284
285 def updater(platform_info, sensor_info, data):
286     #
287     global_atts = {
288         # update times of data contained in file (yyyy-mm-dd HH:MM:SS)
289         # last date in monthly file
290         'end_date' : data['dt'][-1].strftime("%Y-%m-%d %H:%M:%S"),
291         'release_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
292         #
293         'modification_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
294         }
295
296     # data variables
297     # update any variable attributes like range, min, max
298     var_atts = {}
299     # var_atts = {
300     #    'u': {'max': max(data.u),
301     #          'min': min(data.v),
302     #          },
303     #    'v': {'max': max(data.u),
304     #          'min': min(data.v),
305     #          },
306     #    }
307     
308     # subset data only to month being processed (see raw2proc.process())
309     i = data['in']
310
311     # data
312     var_data = (
313         ('time', data['time'][i]),
314         ('u', data['u'][i]),
315         ('v', data['v'][i]),
316        )
317
318     return (global_atts, var_atts, var_data)
319
320 #
321
Note: See TracBrowser for help on using the browser.