source: lmdz_wrf/trunk/tools/create_OBSnetcdf.py @ 261

Last change on this file since 261 was 259, checked in by lfita, 10 years ago

Adding python script to generate netCDF files from ASCII column basd observations

The python script is called as:

python create_OBSnetcdf.py -c [Comchar] -d [DescFile?] -e [Endchar] -f [obsFile] -g [debugFlag] -t [CFreftime],[CFunits] -k [obskind] -s [stloc]

[Comchar]: which characters (':', list) can be used as comment inside the observations file
[DescFile?]: ASCII file with all the information of the observational data-set
[Endchar]: character of end of column
[obsFile]: observatinoal ASCII file with data in columns
[debugFlag]: boolean flag for debugging
[CFreftime],[CFunits]: reference and time-units to transform the date to CF convention
[obskind]: kind of observation, up to now:

'multi-points': multiple individual punctual obs (e.g., lightning strikes)

A new variale called 'obsmap' with the number of observations at a grid mesh of 100x100 will be added

'single-station': single station on a fixed position
'trajectory': following a trajectory

A new variale called 'obstrj' with the number of observations at a grid mesh of 100x100x100 will be

added

[stloc]: only with [obskind]='single-station', a ',' list of longitude,latitude,height will be used to

add new variables on the netCDF file with the given values ('lon','lat','height') or with 'st' header
if these variables already exist

Inside the [DescFile?] you will find:

institution=Institution who creates the data
department=Department within the institution
scientists=names of the data producers
contact=contact of the data producers
description=description of the observations
acknowledgement=sentence of acknowlegement
comment=comment for the measurements

MissingValue?='|' list of missing values (strings as they are in the ASCII file) within the data
comment=comments

varN='|' list of variable names
varLN='|' list of long variable names
varU='|' list units of the variables
varBUFR='|' list BUFR code of the variables
varTYPE='|' list of variable types ('D', 'F', 'I', 'I64', 'S')

NAMElon=name of the variable with the longitude (x position)
NAMElat=name of the variable with the latitude (y position)
NAMEheight=name of the variable with the heights (z position)
NAMEtime=name of the varibale with the time
FMTtime=format of the time (available: as in 'C'[%Y,...], 'CFtime' for already CF-like time)

  • All the values before 'varN' are stored as global attributes
  • All the content as 'var[N/LN/U/BUFR/TYPE]' is used to transform the data
  • [MissingValue?] is used during the transformation of data and then accordingly to the variable type the correspondant '_FillValue' is assigned
  • NAME[lon/lat/height/time]: are used as reference for the netCFD file (in this version only 'NAMEtime')

[NAMEtime] is used to compute the CF-time axis. It can be a combination of different columns (see atached files), whereas [FMTtime] will be used to transform the correspondant time. (How ever, python can not deal with the 7th decimal of the microseconds, so anbything smalloer is lost during the conversion. For all the rest I think is fine). For times already in CF-compilant, FMTtime=CFtime

As always is easier if you see and example (see atached). I called it in this case as:

python create_OBSnetcdf.py -c '#' -d description.dat -e space -f ATDnet_data_121018.csv -t 19491201000000,seconds -k multi-stations

File size: 32.8 KB
Line 
1# -*- coding: iso-8859-15 -*-
2# Generic program to transfrom ASCII observational data in columns to a netcdf
3# L. Fita, LMD February 2015
4## e.g. # create_OBSnetcdf.py -c '#' -d ACAR/description.dat -e space -f ACAR/2012/10/ACAR_121018.asc -g true -t 19491201000000,seconds -k trajectory
5
6import numpy as np
7from netCDF4 import Dataset as NetCDFFile
8import os
9import re
10from optparse import OptionParser
11
12# version
13version=1.1
14
15# Filling values for floats, integer and string
16fillValueF = 1.e20
17fillValueI = -99999
18fillValueS = '---'
19
20# Length of the string variables
21StringLength = 200
22
23# Size of the map for the complementary variables/maps
24Ndim2D = 100
25
26main = 'create_OBSnetcdf.py'
27errormsg = 'ERROR -- error -- ERROR -- error'
28warnmsg = 'WARNING -- warning -- WARNING -- warning'
29
30fillValue = 1.e20
31
32def searchInlist(listname, nameFind):
33    """ Function to search a value within a list
34    listname = list
35    nameFind = value to find
36    >>> searInlist(['1', '2', '3', '5'], '5')
37    True
38    """
39    for x in listname:
40      if x == nameFind:
41        return True
42    return False
43
44def set_attribute(ncvar, attrname, attrvalue):
45    """ Sets a value of an attribute of a netCDF variable. Removes previous attribute value if exists
46    ncvar = object netcdf variable
47    attrname = name of the attribute
48    attrvalue = value of the attribute
49    """
50    import numpy as np
51    from netCDF4 import Dataset as NetCDFFile
52
53    attvar = ncvar.ncattrs()
54    if searchInlist(attvar, attrname):
55        attr = ncvar.delncattr(attrname)
56
57    attr = ncvar.setncattr(attrname, attrvalue)
58
59    return ncvar
60
61def basicvardef(varobj, vstname, vlname, vunits):
62    """ Function to give the basic attributes to a variable
63    varobj= netCDF variable object
64    vstname= standard name of the variable
65    vlname= long name of the variable
66    vunits= units of the variable
67    """
68    attr = varobj.setncattr('standard_name', vstname)
69    attr = varobj.setncattr('long_name', vlname)
70    attr = varobj.setncattr('units', vunits)
71
72def remove_NONascii(string):
73    """ Function to remove that characters which are not in the standard 127 ASCII
74      string= string to transform
75    >>> remove_NONascii('Lluís')
76    Lluis
77    """
78    fname = 'remove_NONascii'
79
80    newstring = string
81
82    RTFchar= ['á', 'é', 'í', 'ó', 'ú', 'à', 'Ú', 'ì', 'ò', 'ù', 'â', 'ê', 'î', 'ÃŽ',  \
83      'û', 'À', 'ë', 'ï', 'ö', 'ÃŒ', 'ç', 'ñ','Ê', 'œ', 'Á', 'É', 'Í', 'Ó', 'Ú', 'À', \
84      'È', 'Ì', 'Ò', 'Ù', 'Â', 'Ê', 'Î', 'Ô', 'Û', 'Ä', 'Ë', 'Ï', 'Ö', 'Ü', 'Ç', 'Ñ',\
85      'Æ', 'Œ', '\n', '\t']
86    ASCchar= ['a', 'e', 'i', 'o', 'u', 'a', 'e', 'i', 'o', 'u', 'a', 'e', 'i', 'o',  \
87      'u', 'a', 'e', 'i', 'o', 'u', 'c', 'n','ae', 'oe', 'A', 'E', 'I', 'O', 'U', 'A', \
88      'E', 'I', 'O', 'U', 'A', 'E', 'I', 'O', 'U', 'A', 'E', 'I', 'O', 'U', 'C', 'N',\
89      'AE', 'OE', '', ' ']
90
91    Nchars = len(RTFchar)
92    for ichar in range(Nchars):
93        foundchar = string.find(RTFchar[ichar])
94        if foundchar != 0:
95            newstring = newstring.replace(RTFchar[ichar], ASCchar[ichar])
96
97    return newstring
98
99def read_description(fdobs, dbg):
100    """ reads the description file of the observational data-set
101    fdobs= descriptive observational data-set
102    dbg= boolean argument for debugging
103    * Each station should have a 'description.dat' file with:
104      institution=Institution who creates the data
105      department=Department within the institution
106      scientists=names of the data producers
107      contact=contact of the data producers
108      description=description of the observations
109      acknowledgement=sentence of acknowlegement
110      comment=comment for the measurements
111
112      MissingValue='|' list of ASCII values for missing values within the data
113        (as they appear!)
114      comment=comments
115
116      varN='|' list of variable names
117      varLN='|' list of long variable names
118      varU='|' list units of the variables
119      varBUFR='|' list BUFR code of the variables
120      varTYPE='|' list of variable types ('D', 'F', 'I', 'I64', 'S')
121
122      NAMElon=name of the variable with the longitude (x position)
123      NAMElat=name of the variable with the latitude (y position)
124      NAMEheight=ind_alt
125      NAMEtime=name of the varibale with the time
126      FMTtime=format of the time (as in 'C', 'CFtime' for already CF-like time)
127
128    """
129    fname = 'read_description'
130
131    descobj = open(fdobs, 'r')
132    desc = {}
133
134    namevalues = []
135
136    for line in descobj:
137        if line[0:1] != '#' and len(line) > 1 :
138            descn = remove_NONascii(line.split('=')[0])
139            descv = remove_NONascii(line.split('=')[1])
140            namevalues.append(descn)
141            if descn[0:3] != 'var':
142                if descn != 'MissingValue':
143                    desc[descn] = descv
144                else:
145                    desc[descn] = []
146                    for dn in descv.split('|'): desc[descn].append(dn)
147                    print '  ' + fname + ': missing values found:',desc[descn]
148            elif descn[0:3] == 'var':
149                desc[descn] = descv.split('|')
150            elif descn[0:4] == 'NAME':
151                desc[descn] = descv
152            elif descn[0:3] == 'FMT':
153                desc[descn] = descv
154
155    if dbg:
156        Nvars = len(desc['varN'])
157        print '  ' + fname + ": description content of '" + fdobs + "'______________"
158        for varn in namevalues:
159            if varn[0:3] != 'var':
160                print '    ' + varn + ':',desc[varn]
161            elif varn == 'varN':
162                print '    * Variables:'
163                for ivar in range(Nvars):
164                    varname = desc['varN'][ivar]
165                    varLname = desc['varLN'][ivar]
166                    varunits = desc['varU'][ivar]
167                    if desc.has_key('varBUFR'): 
168                        varbufr=[ivar]
169                        print '      ', ivar, varname + ':',varLname,'[',varunits,   \
170                          ']','bufr code:',varbufr
171                    else:
172                        print '      ', ivar, varname + ':',varLname,'[',varunits,']'
173
174    descobj.close()
175
176    return desc
177
178def value_fmt(val, miss, fmt):
179    """ Function to transform an ASCII value to a given format
180      val= value to transform
181      miss= list of possible missing values
182      fmt= format to take:
183        'D': float double precission
184        'F': float
185        'I': integer
186        'I64': 64-bits integer
187        'S': string
188    >>> value_fmt('9876.12', '-999', 'F')
189    9876.12
190    """
191
192    fname = 'value_fmt'
193
194    fmts = ['D', 'F', 'I', 'I64', 'S']
195    Nfmts = len(fmts)
196
197    if not searchInlist(miss,val):
198        if not searchInlist(fmts, fmt):
199            print errormsg
200            print '  ' + fname + ": format '" + fmt + "' not ready !!"
201            quit(-1)
202        else:
203            if fmt == 'D':
204                newval = np.float32(val)
205            elif fmt == 'F':
206                newval = np.float(val)
207            elif fmt == 'I':
208                newval = int(val)
209            elif fmt == 'I64':
210                newval = np.int64(val)
211            elif fmt == 'S':
212                newval = val
213    else:
214        newval = None
215
216    return newval
217
218def read_datavalues(dataf, comchar, colchar, fmt, miss, varns, dbg):
219    """ Function to read from an ASCII file values in column
220      dataf= data file
221      comchar= list of the characters indicating comment in the file
222      colchar= character which indicate end of value in the column
223      dbg= debug mode or not
224      fmt= list of kind of values to be found
225      miss= missing value
226      varns= list of name of the variables to find
227    """
228
229    fname = 'read_datavalues'
230
231    ofile = open(dataf, 'r')
232    Nvals = len(fmt)
233
234    finalvalues = {}
235
236    iline = 0
237    for line in ofile:
238        line = line.replace('\n','').replace(chr(13),'')
239        if not searchInlist(comchar,line[0:1]) and len(line) > 1:
240            values0 = line.split(colchar)
241# Removing no-value columns
242            values = []
243            for iv in values0:
244                if len(iv) > 0: values.append(iv)
245
246            Nvalues = len(values)
247# Checkings for wierd characters at the end of lines (use it to check)
248#            if values[Nvalues-1][0:4] == '-999':
249#                print line,'last v:',values[Nvalues-1],'len:',len(values[Nvalues-1])
250#                for ic in range(len(values[Nvalues-1])):
251#                    print ic,ord(values[Nvalues-1][ic:ic+1])
252#                quit()
253
254            if len(values[Nvalues-1]) == 0:
255                Nvalues = Nvalues - 1
256               
257            if Nvalues != Nvals:
258                print warnmsg
259                print '  ' + fname + ': number of formats:',Nvals,' and number of ', \
260                  'values:',Nvalues,' with split character *' + colchar +            \
261                  '* does not coincide!!'
262                print '    * what is found is ________'
263                if Nvalues > Nvals:
264                    Nshow = Nvals
265                    for ivar in range(Nshow):
266                        print '    ',varns[ivar],'fmt:',fmt[ivar],'value:',values[ivar]
267                    print '      missing formats for:',values[Nshow:Nvalues+1]
268                    print '      values not considered, continue'
269                else:
270                    Nshow = Nvalues
271                    for ivar in range(Nshow):
272                        print '   ',varns[ivar],'fmt:',fmt[ivar],'value:',values[ivar]
273                    print '      excess of formats:',fmt[Nshow:Nvals+1]
274                    quit(-1)
275
276# Reading and transforming values
277            if dbg: print '  ' + fname + ': values found _________'
278
279            for ivar in range(Nvals):
280                if dbg: 
281                    print iline, varns[ivar],'value:',values[ivar],miss,fmt[ivar]
282
283                if iline == 0:
284                    listvals = []
285                    listvals.append(value_fmt(values[ivar], miss, fmt[ivar]))
286                    finalvalues[varns[ivar]] = listvals
287                else:
288                    listvals = finalvalues[varns[ivar]]
289                    listvals.append(value_fmt(values[ivar], miss, fmt[ivar]))
290                    finalvalues[varns[ivar]] = listvals
291        else:
292# First line without values
293            if iline == 0: iline = -1
294   
295        iline = iline + 1
296
297    ofile.close()
298
299    return finalvalues
300
301def writing_str_nc(varo, values, Lchar):
302    """ Function to write string values in a netCDF variable as a chain of 1char values
303    varo= netCDF variable object
304    values = list of values to introduce
305    Lchar = length of the string in the netCDF file
306    """
307
308    Nvals = len(values)
309    for iv in range(Nvals):   
310        stringv=values[iv] 
311        charvals = np.chararray(Lchar)
312        Lstr = len(stringv)
313        charvals[Lstr:Lchar] = ''
314
315        for ich in range(Lstr):
316            charvals[ich] = stringv[ich:ich+1]
317
318        varo[iv,:] = charvals
319
320    return
321
322def Stringtimes_CF(tvals, fmt, Srefdate, tunits, dbg):
323    """ Function to transform a given data in String formats to a CF date
324      tvals= string temporal values
325      fmt= format of the the time values
326      Srefdate= reference date in [YYYY][MM][DD][HH][MI][SS] format
327      tunits= units to use ('weeks', 'days', 'hours', 'minutes', 'seconds')
328      dbg= debug
329    >>> Stringtimes_CF(['19760217082712','20150213101837'], '%Y%m%d%H%M%S',
330      '19491201000000', 'hours', False)
331    [229784.45333333  571570.31027778]
332    """
333    import datetime as dt
334
335    fname = 'Stringtimes'
336
337    dimt = len(tvals)
338
339    yrref = int(Srefdate[0:4])
340    monref = int(Srefdate[4:6])
341    dayref = int(Srefdate[6:8])
342    horref = int(Srefdate[8:10])
343    minref = int(Srefdate[10:12])
344    secref = int(Srefdate[12:14])
345    refdate = dt.datetime( yrref, monref, dayref, horref, minref, secref)
346
347    cftimes = np.zeros((dimt), dtype=np.float)
348
349    Nfmt=len(fmt.split('%'))
350
351    if dbg: print '  ' + fname + ': fmt=',fmt,'refdate:',Srefdate,'uits:',tunits,    \
352      'date dt_days dt_time deltaseconds CFtime _______'
353    for it in range(dimt):
354
355# Removing excess of mili-seconds (up to 6 decimals)
356        if fmt.split('%')[Nfmt-1] == 'f':
357            tpoints = tvals[it].split('.')
358            if len(tpoints[len(tpoints)-1]) > 6:
359                milisec = '{0:.6f}'.format(np.float('0.'+tpoints[len(tpoints)-1]))[0:7]
360                newtval = ''
361                for ipt in range(len(tpoints)-1):
362                    newtval = newtval + tpoints[ipt] + '.'
363                newtval = newtval + str(milisec)[2:len(milisec)+1]
364            else:
365                newtval = tvals[it]
366            tval = dt.datetime.strptime(newtval, fmt)
367        else:
368            tval = dt.datetime.strptime(tvals[it], fmt)
369
370        deltatime = tval - refdate
371        deltaseconds = deltatime.days*24.*3600. + deltatime.seconds +                \
372          deltatime.microseconds/100000.
373        if tunits == 'weeks':
374            deltat = 7.*24.*3600.
375        elif tunits == 'days':
376            deltat = 24.*3600.
377        elif tunits == 'hours':
378            deltat = 3600.
379        elif tunits == 'minutes':
380            deltat = 60.
381        elif tunits == 'seconds':
382            deltat = 1.
383        else:
384            print errormsg
385            print '  ' + fname + ": time units '" + tunits + "' not ready !!"
386            quit(-1)
387
388        cftimes[it] = deltaseconds / deltat
389        if dbg:
390            print '  ' + tvals[it], deltatime, deltaseconds, cftimes[it]
391
392    return cftimes
393
394def adding_complementary(onc, dscn, okind, dvalues, tvals, refCFt, CFtu, Nd, dbg):
395    """ Function to add complementary variables as function of the observational type
396      onc= netcdf objecto file to add the variables
397      dscn= description dictionary
398      okind= observational kind
399      dvalues= values
400      tvals= CF time values
401      refCFt= reference time of CF time (in [YYYY][MM][DD][HH][MI][SS])
402      CFtu= CF time units
403      Nd= size of the domain
404      dbg= debugging flag
405    """
406    import numpy.ma as ma
407
408    fname = 'adding_complementary'
409 
410# Kind of observations which require de integer lon/lat (for the 2D map)
411    map2D=['multi-points', 'trajectory']
412
413    SrefCFt = refCFt[0:4] +'-'+ refCFt[4:6] +'-'+ refCFt[6:8] + ' ' + refCFt[8:10] + \
414      ':'+ refCFt[10:12] +':'+ refCFt[12:14]
415
416    if dscn['NAMElon'] == '-' or dscn['NAMElat'] == '-':
417        print errormsg
418        print '  ' + fname + ": to complement a '" + okind + "' observation kind " + \
419          " a given longitude ('NAMElon':",dscn['NAMElon'],") and latitude ('" +     \
420          "'NAMElat:'", dscn['NAMElat'],') from the data has to be provided and ' +  \
421          'any are given !!'
422        quit(-1)
423
424    if not dvalues.has_key(dscn['NAMElon']) or not dvalues.has_key(dscn['NAMElat']):
425        print errormsg
426        print '  ' + fname + ": observations do not have 'NAMElon':",                \
427          dscn['NAMElon'],"and/or 'NAMElat:'", dscn['NAMElat'],' !!'
428        print '    available data:',dvalues.keys()
429        quit(-1)
430
431    if okind == 'trajectory':
432        if dscn['NAMEheight'] == '-':
433            print warnmsg
434            print '  ' + fname + ": to complement a '" + okind + "' observation " +  \
435              "kind a given height ('NAMEheight':",dscn['NAMEheight'],"') might " +  \
436              'be provided and any is given !!'
437            quit(-1)
438
439        if not dvalues.has_key(dscn['NAMEheight']):
440            print errormsg
441            print '  ' + fname + ": observations do not have 'NAMEtime':",           \
442              dscn['NAMEtime'],' !!'
443            print '    available data:',dvalues.keys()
444            quit(-1)
445
446    if searchInlist(map2D, okind):
447# A new 2D map with the number of observation will be added for that 'NAMElon'
448#   and 'NAMElat' are necessary. A NdxNd domain space size will be used.
449        objfile.createDimension('lon2D',Nd)
450        objfile.createDimension('lat2D',Nd)
451        lonvals = ma.masked_equal(dvalues[dscn['NAMElon']], None)
452        latvals = ma.masked_equal(dvalues[dscn['NAMElat']], None)
453
454        minlon = min(lonvals)
455        maxlon = max(lonvals)
456        minlat = min(latvals)
457        maxlat = max(latvals)
458
459        blon = (maxlon - minlon)/(Nd-1)
460        blat = (maxlat - minlat)/(Nd-1)
461
462        newvar = onc.createVariable( 'lon2D', 'f8', ('lon2D'))
463        basicvardef(newvar, 'longitude', 'longitude map observations','degrees_East')
464        newvar[:] = minlon + np.arange(Nd)*blon
465        newattr = set_attribute(newvar, 'axis', 'X')
466
467        newvar = onc.createVariable( 'lat2D', 'f8', ('lat2D'))
468        basicvardef(newvar, 'latitude', 'latitude map observations', 'degrees_North')
469        newvar[:] = minlat + np.arange(Nd)*blat
470        newattr = set_attribute(newvar, 'axis', 'Y')
471
472        if dbg: 
473            print '  ' + fname + ': minlon=',minlon,'maxlon=',maxlon
474            print '  ' + fname + ': minlat=',minlat,'maxlat=',maxlat
475            print '  ' + fname + ': precission on x-axis=', blon*(Nd-1), 'y-axis=',  \
476              blat*(Nd-1)
477       
478    if okind == 'multi-points':
479        map2D = np.ones((Nd, Nd), dtype=np.float)*fillValueI
480
481        dimt = len(tvals)
482        Nlost = 0
483        for it in range(dimt):
484            lon = dvalues[dscn['NAMElon']][it]
485            lat = dvalues[dscn['NAMElat']][it]
486            if lon is not None and lat is not None:
487                ilon = int((Nd-1)*(lon - minlon)/(maxlon - minlon))
488                ilat = int((Nd-1)*(lat - minlat)/(maxlat - minlat))
489
490                if map2D[ilat,ilon] == fillValueI: 
491                    map2D[ilat,ilon] = 1
492                else:
493                    map2D[ilat,ilon] = map2D[ilat,ilon] + 1
494                if dbg: print it, lon, lat, ilon, ilat, map2D[ilat,ilon]
495
496        newvar = onc.createVariable( 'mapobs', 'f4', ('lat2D', 'lon2D'),             \
497          fill_value = fillValueI)
498        basicvardef(newvar, 'map_observations', 'number of observations', '-')
499        newvar[:] = map2D
500        newattr = set_attribute(newvar, 'coordinates', 'lon2D lat2D')
501
502    elif okind == 'trajectory':
503# A new 2D map with the trajectory 'NAMElon' and 'NAMElat' and maybe 'NAMEheight'
504#   are necessary. A NdxNdxNd domain space size will be used. Using time as
505#   reference variable
506        if dscn['NAMEheight'] == '-':
507# No height
508            map2D = np.ones((Nd, Nd), dtype=np.float)*fillValueI
509
510            dimt = len(tvals)
511            Nlost = 0
512            if dbg: print ' time-step lon lat ix iy passes _______'
513            for it in range(dimt):
514                lon = dvalues[dscn['NAMElon']][it]
515                lat = dvalues[dscn['NAMElat']][it]
516                if lon is  not None and lat is not None:
517                    ilon = int((Nd-1)*(lon - minlon)/(maxlon - minlon))
518                    ilat = int((Nd-1)*(lat - minlat)/(maxlat - minlat))
519
520                    if map2D[ilat,ilon] == fillValueI: 
521                        map2D[ilat,ilon] = 1
522                    else:
523                        map2D[ilat,ilon] = map2D[ilat,ilon] + 1
524                    if dbg: print it, lon, lat, ilon, ilat, map2D[ilat,ilon]
525
526            newvar = onc.createVariable( 'trjobs', 'i', ('lat2D', 'lon2D'),          \
527              fill_value = fillValueI)
528            basicvardef(newvar, 'trajectory_observations', 'number of passes', '-' )
529            newvar[:] = map2D
530            newattr = set_attribute(newvar, 'coordinates', 'lon2D lat2D')
531
532        else:
533            ivn = 0
534            for vn in dscn['varN']:
535                if vn == dscn['NAMEheight']: 
536                    zu = dscn['varU'][ivn]
537                    break
538                ivn = ivn + 1
539                   
540            objfile.createDimension('z2D',Nd)
541            zvals = ma.masked_equal(dvalues[dscn['NAMEheight']], None)
542            minz = min(zvals)
543            maxz = max(zvals)
544
545            bz = (maxz - minz)/(Nd-1)
546
547            newvar = onc.createVariable( 'z2D', 'f8', ('z2D'))
548            basicvardef(newvar, 'z2D', 'z-coordinate map observations', zu)
549            newvar[:] = minz + np.arange(Nd)*bz
550            newattr = set_attribute(newvar, 'axis', 'Z')
551
552            if dbg:
553                print '  ' + fname + ': zmin=',minz,zu,'zmax=',maxz,zu
554                print '  ' + fname + ': precission on z-axis=', bz*(Nd-1), zu
555
556            map3D = np.ones((Nd, Nd, Nd), dtype=int)*fillValueI
557            dimt = len(tvals)
558            Nlost = 0
559            if dbg: print ' time-step lon lat z ix iy iz passes _______'
560            for it in range(dimt):
561                lon = dvalues[dscn['NAMElon']][it]
562                lat = dvalues[dscn['NAMElat']][it]
563                z = dvalues[dscn['NAMEheight']][it]
564                if lon is  not None and lat is not None and z is not None:
565                    ilon = int((Nd-1)*(lon - minlon)/(maxlon - minlon))
566                    ilat = int((Nd-1)*(lat - minlat)/(maxlat - minlat))
567                    iz = int((Nd-1)*(z - minz)/(maxz - minz))
568
569                    if map3D[iz,ilat,ilon] == fillValueI: 
570                        map3D[iz,ilat,ilon] = 1
571                    else:
572                        map3D[iz,ilat,ilon] = map3D[iz,ilat,ilon] + 1
573                    if dbg: print it, lon, lat, z, ilon, ilat, iz, map3D[iz,ilat,ilon]
574
575            newvar = onc.createVariable( 'trjobs', 'i', ('z2D', 'lat2D', 'lon2D'),   \
576              fill_value = fillValueI)
577            basicvardef(newvar, 'trajectory_observations', 'number of passes', '-')
578            newvar[:] = map3D
579            newattr = set_attribute(newvar, 'coordinates', 'lon2D lat2D z2D')
580
581    onc.sync()
582    return
583
584def adding_station_desc(onc,stdesc):
585    """ Function to add a station description in a netCDF file
586      onc= netCDF object
587      stdesc= station description lon, lat, height
588    """
589    fname = 'adding_station_desc'
590
591    newdim = onc.createDimension('nst',1)
592
593    if onc.variables.has_key('lon'):
594        print warnmsg
595        print '  ' + fname + ": variable 'lon' already exist !!"
596        print "    renaming it as 'lonst'"
597        lonname = 'lonst'
598    else:
599        lonname = 'lon'
600
601    newvar = objfile.createVariable( lonname, 'f4', ('nst'))
602    basicvardef(newvar, lonname, 'longitude', 'degrees_West' )
603    newvar[:] = stdesc[0]
604
605    if onc.variables.has_key('lat'):
606        print warnmsg
607        print '  ' + fname + ": variable 'lat' already exist !!"
608        print "    renaming it as 'latst'"
609        latname = 'latst'
610    else:
611        latname = 'lat'
612
613    newvar = objfile.createVariable( latname, 'f4', ('nst'))
614    basicvardef(newvar, lonname, 'latitude', 'degrees_North' )
615    newvar[:] = stdesc[1]
616
617    if onc.variables.has_key('height'):
618        print warnmsg
619        print '  ' + fname + ": variable 'height' already exist !!"
620        print "    renaming it as 'heightst'"
621        heightname = 'heightst'
622    else:
623        heightname = 'height'
624
625    newvar = objfile.createVariable( heightname, 'f4', ('nst'))
626    basicvardef(newvar, heightname, 'height above sea level', 'm' )
627    newvar[:] = stdesc[2]
628
629    return
630
631####### ###### ##### #### ### ## #
632
633strCFt="Refdate,tunits (CF reference date [YYYY][MM][DD][HH][MI][SS] format and " +  \
634  " and time units: 'weeks', 'days', 'hours', 'miuntes', 'seconds')"
635
636kindobs=['multi-points', 'single-station', 'trajectory']
637strkObs="kind of observations; 'multi-points': multiple individual punctual obs " +  \
638  "(e.g., lightning strikes), 'single-station': single station on a fixed position,"+\
639  "'trajectory': following a trajectory"
640
641parser = OptionParser()
642parser.add_option("-c", "--comments", dest="charcom",
643  help="':', list of characters used for comments", metavar="VALUES")
644parser.add_option("-d", "--descriptionfile", dest="fdesc",
645  help="description file to use", metavar="FILE")
646parser.add_option("-e", "--end_column", dest="endcol",
647  help="character to indicate end of the column ('space', for ' ')", metavar="VALUE")
648parser.add_option("-f", "--file", dest="obsfile",
649  help="observational file to use", metavar="FILE")
650parser.add_option("-g", "--debug", dest="debug",
651  help="whther debug is required ('false', 'true')", metavar="VALUE")
652parser.add_option("-k", "--kindObs", dest="obskind", type='choice', choices=kindobs, 
653  help=strkObs, metavar="FILE")
654parser.add_option("-s", "--stationLocation", dest="stloc", 
655  help="longitude, latitude and height of the station (only for 'single-station')", 
656  metavar="FILE")
657parser.add_option("-t", "--CFtime", dest="CFtime", help=strCFt, metavar="VALUE")
658
659(opts, args) = parser.parse_args()
660
661#######    #######
662## MAIN
663    #######
664
665ofile='OBSnetcdf.nc'
666
667if opts.charcom is None:
668    print warnmsg
669    print '  ' + main + ': No list of comment characters provided!!'
670    print '    assuming no need!'
671    charcomments = []
672else:
673    charcomments = opts.charcom.split(':')   
674
675if opts.fdesc is None:
676    print errormsg
677    print '  ' + main + ': No description file for the observtional data provided!!'
678    quit(-1)
679
680if opts.endcol is None:
681    print warnmsg
682    print '  ' + main + ': No list of comment characters provided!!'
683    print "    assuming 'space'"
684    endcol = ' '
685else:
686    if opts.endcol == 'space':
687        endcol = ' '
688    else:
689        endcol = opts.endcol
690
691if opts.obsfile is None:
692    print errormsg
693    print '  ' + main + ': No observtions file provided!!'
694    quit(-1)
695
696if opts.debug is None:
697    print warnmsg
698    print '  ' + main + ': No debug provided!!'
699    print "    assuming 'False'"
700    debug = False
701else:
702    if opts.debug == 'true':
703        debug = True
704    else:
705        debug = False
706
707if not os.path.isfile(opts.fdesc):
708    print errormsg
709    print '   ' + main + ": description file '" + opts.fdesc + "' does not exist !!"
710    quit(-1)
711
712if not os.path.isfile(opts.obsfile):
713    print errormsg
714    print '   ' + main + ": observational file '" + opts.obsfile + "' does not exist !!"
715    quit(-1)
716
717if opts.CFtime is None:
718    print warnmsg
719    print '  ' + main + ': No CFtime criteria are provided !!'
720    print "    either time is already in CF-format ('timeFMT=CFtime') in '" +        \
721      opts.fdesc + "'"
722    print "    or assuming refdate: '19491201000000' and time units: 'hours'"
723    referencedate = '19491201000000'
724    timeunits = 'hours'
725else:
726    referencedate = opts.CFtime.split(',')[0]
727    timeunits = opts.CFtime.split(',')[1]
728
729if opts.obskind is None:
730    print warnmsg
731    print '  ' + main + ': No kind of observations provided !!'
732    print "    assuming 'single-station': single station on a fixed position at 0,0,0"
733    obskind = 'single-station'
734    stationdesc = [0.0, 0.0, 0.0]
735else:
736    obskind = opts.obskind
737    if obskind == 'single-station':
738        if opts.stloc is None:
739            print errornmsg
740            print '  ' + main + ': No station location provided !!'
741            quit(-1)
742        else:
743            stationdesc = [np.float(opts.stloc.split(',')[0]),                       \
744              np.float(opts.stloc.split(',')[1]), np.float(opts.stloc.split(',')[2])]
745    else:
746        obskind = opts.obskind
747
748# Reading description file
749##
750description = read_description(opts.fdesc, debug)
751
752Nvariables=len(description['varN'])
753formats = description['varTYPE']
754
755if len(formats) != Nvariables: 
756    print errormsg
757    print '  ' + main + ': number of formats:',len(formats),' and number of ' +     \
758      'variables', Nvariables,' does not coincide!!'
759    print '    * what is found is _______'
760    if Nvariables > len(formats):
761        Nshow = len(formats)
762        for ivar in range(Nshow):
763            print '      ',description['varN'][ivar],':', description['varLN'][ivar],\
764               '[', description['varU'][ivar], '] fmt:', formats[ivar]
765        print '      missing values for:', description['varN'][Nshow:Nvariables+1]
766    else:
767        Nshow = Nvariables
768        for ivar in range(Nshow):
769            print '      ',description['varN'][ivar],':', description['varLN'][ivar],\
770               '[', description['varU'][ivar], '] fmt:', formats[ivar]
771        print '      excess of formats for:', formats[Nshow:len(formats)+1]
772
773    quit(-1)
774
775# Reading values
776##
777datavalues = read_datavalues(opts.obsfile, charcomments, endcol, formats, 
778  description['MissingValue'], description['varN'], debug)
779
780# Total number of values
781Ntvalues = len(datavalues[description['varN'][0]])
782print main + ': total temporal values found:',Ntvalues
783
784objfile = NetCDFFile(ofile, 'w')
785 
786# Creation of dimensions
787##
788objfile.createDimension('time',None)
789objfile.createDimension('StrLength',StringLength)
790
791# Creation of variables
792##
793for ivar in range(Nvariables):
794    varn = description['varN'][ivar]
795    print "  including: '" + varn + "' ... .. ."
796
797    if formats[ivar] == 'D':
798        newvar = objfile.createVariable(varn, 'f32', ('time'), fill_value=fillValueF)
799        basicvardef(newvar, varn, description['varLN'][ivar],                        \
800          description['varU'][ivar])
801        newvar[:] = np.where(datavalues[varn] is None, fillValueF, datavalues[varn])
802    elif formats[ivar] == 'F':
803        newvar = objfile.createVariable(varn, 'f', ('time'), fill_value=fillValueF)
804        basicvardef(newvar, varn, description['varLN'][ivar],                        \
805          description['varU'][ivar])
806        newvar[:] = np.where(datavalues[varn] is None, fillValueF, datavalues[varn])
807    elif formats[ivar] == 'I':
808        newvar = objfile.createVariable(varn, 'i', ('time'), fill_value=fillValueI)
809        basicvardef(newvar, varn, description['varLN'][ivar],                        \
810          description['varU'][ivar])
811# Why is not wotking with integers?
812        vals = np.array(datavalues[varn])
813        for iv in range(Ntvalues):
814            if vals[iv] is None: vals[iv] = fillValueI
815        newvar[:] = vals
816    elif formats[ivar] == 'S':
817        newvar = objfile.createVariable(varn, 'c', ('time','StrLength'))
818        basicvardef(newvar, varn, description['varLN'][ivar],                        \
819          description['varU'][ivar])
820        writing_str_nc(newvar, datavalues[varn], StringLength)
821
822# Extra variable descriptions/attributes
823    if description.has_key('varBUFR'): 
824        set_attribute(newvar,'bufr_code',description['varBUFR'][ivar])
825
826    objfile.sync()
827
828# Time variable in CF format
829##
830print description['FMTtime'], 'CFtime'
831if description['FMTtime'] == 'CFtime':
832    timevals = datavalues[description['NAMEtime']]
833    iv = 0
834    for ivn in description['varN']:
835        if ivn == description['NAMEtime']:
836            tunits = description['varU'][iv]
837            break
838        iv = iv + 1
839else:
840    # Time as a composition of different columns
841    tcomposite = description['NAMEtime'].find('@')
842    if tcomposite != 0:
843        timevars = description['NAMEtime'].split('@')
844
845        print warnmsg
846        print '  ' + main + ': time values as combination of different columns!'
847        print '    combining:',timevars,' with a final format: ',description['FMTtime']
848
849        timeSvals = []
850        if debug: print '  ' + main + ': creating times _______'
851        for it in range(Ntvalues):
852            tSvals = ''
853            for tvar in timevars:
854                tSvals = tSvals + datavalues[tvar][it] + ' '
855
856            timeSvals.append(tSvals[0:len(tSvals)-1])
857            if debug: print it, '*' + timeSvals[it] + '*'
858
859        timevals = Stringtimes_CF(timeSvals, description['FMTtime'], referencedate,      \
860          timeunits, debug)
861    else:
862        timevals = Stringtimes_CF(datavalues[description['NAMEtime']],                   \
863          description['FMTtime'], referencedate, timeunits, debug)
864
865    CFtimeRef = referencedate[0:4] +'-'+ referencedate[4:6] +'-'+ referencedate[6:8] +   \
866      ' ' + referencedate[8:10] +':'+ referencedate[10:12] +':'+ referencedate[12:14]
867    tunits = timeunits + ' since ' + CFtimeRef
868
869if objfile.variables.has_key('time'):
870    print warnmsg
871    print '  ' + main + ": variable 'time' already exist !!"
872    print "    renaming it as 'CFtime'"
873    timeCFname = 'CFtime'
874    newdim = objfile.renameDimension('time','CFtime')
875    newvar = objfile.createVariable( timeCFname, 'f8', ('CFtime'))
876    basicvardef(newvar, timeCFname, 'time', tunits )
877else:
878    timeCFname = 'time'
879    newvar = objfile.createVariable( timeCFname, 'f8', ('time'))
880    basicvardef(newvar, timeCFname, 'time', tunits )
881
882set_attribute(newvar, 'calendar', 'standard')
883newvar[:] = timevals
884
885# Global attributes
886##
887for descn in description.keys():
888    if descn[0:3] != 'var' and descn[0:4] != 'NAME' and descn[0:3] != 'FMT':
889        set_attribute(objfile, descn, description[descn])
890
891set_attribute(objfile,'author_nc','Lluis Fita')
892set_attribute(objfile,'institution_nc','Laboratoire de Meteorology Dynamique, ' +    \
893  'LMD-Jussieu, UPMC, Paris')
894set_attribute(objfile,'country_nc','France')
895set_attribute(objfile,'script_nc','create_OBSnetcdf.py')
896set_attribute(objfile,'version_script',version)
897set_attribute(objfile,'information',                                                 \
898  'http://www.lmd.jussieu.fr/~lflmd/ASCIIobs_nc/index.html')
899
900objfile.sync()
901
902# Adding new variables as function of the observational type
903## 'multi-points', 'single-station', 'trajectory'
904
905if obskind != 'single-station':
906    adding_complementary(objfile, description, obskind, datavalues, timevals,        \
907      referencedate, timeunits, Ndim2D, debug)
908else:
909# Adding three variables with the station location, longitude, latitude and height
910    adding_station_desc(objfile,stationdesc)
911
912objfile.sync()
913objfile.close()
914
915print main + ": Successfull generation of netcdf observational file '" + ofile + "' !!"
Note: See TracBrowser for help on using the repository browser.