source: flex_extract.git/python/plot_retrieved.py @ ccab809

ctbtodev
Last change on this file since ccab809 was ccab809, checked in by Anne Philipp <anne.philipp@…>, 6 years ago

further improved plotting routine for date selection

  • Property mode set to 100755
File size: 22.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#************************************************************************
4# TODO AP
5# - documentation der Funktionen
6# - docu der progam functionality
7# - apply pep8
8#************************************************************************
9#*******************************************************************************
10# @Author: Leopold Haimberger (University of Vienna)
11#
12# @Date: November 2015
13#
14# @Change History:
15#
16#    February 2018 - Anne Philipp (University of Vienna):
17#        - applied PEP8 style guide
18#        - added documentation
19#        - created function main and moved the two function calls for
20#          arguments and plotting into it
21#        - added function getBasics to extract the boundary conditions
22#          of the data fields from the first grib file it gets.
23#
24# @License:
25#    (C) Copyright 2015-2018.
26#
27#    This software is licensed under the terms of the Apache Licence Version 2.0
28#    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
29#
30# @Program Functionality:
31#    Simple tool for creating maps and time series of retrieved fields.
32#
33# @Program Content:
34#    - main
35#    - getBasics
36#    - plotRetrieved
37#    - plotTS
38#    - plotMap
39#    - getPlotArgs
40#
41#*******************************************************************************
42
43# ------------------------------------------------------------------------------
44# MODULES
45# ------------------------------------------------------------------------------
46import time
47import datetime
48import os
49import inspect
50import sys
51import glob
52from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
53
54from matplotlib.pylab import *
55import matplotlib.patches as mpatches
56from mpl_toolkits.basemap import Basemap, addcyclic
57import matplotlib.colors as mcolors
58from matplotlib.font_manager import FontProperties
59from matplotlib.patches import Polygon
60import matplotlib.cm as cmx
61import matplotlib.colors as colors
62#from rasotools.utils import stats
63
64font = {'family': 'monospace', 'size': 12}
65matplotlib.rcParams['xtick.major.pad'] = '20'
66
67matplotlib.rc('font', **font)
68
69from eccodes import *
70import numpy as np
71
72# add path to pythonpath so that python finds its buddies
73localpythonpath = os.path.dirname(os.path.abspath(
74    inspect.getfile(inspect.currentframe())))
75if localpythonpath not in sys.path:
76    sys.path.append(localpythonpath)
77
78# software specific classes and modules from flex_extract
79from Tools import silentremove
80from ControlFile import ControlFile
81#from GribTools import GribTools
82from UIOFiles import UIOFiles
83
84# ------------------------------------------------------------------------------
85# FUNCTION
86# ------------------------------------------------------------------------------
87def main():
88    '''
89    @Description:
90        If plotRetrieved is called from command line, this function controls
91        the program flow and calls the argumentparser function and
92        the plotRetrieved function for plotting the retrieved GRIB data.
93
94    @Input:
95        <nothing>
96
97    @Return:
98        <nothing>
99    '''
100    args, c = getPlotArgs()
101    plotRetrieved(args, c)
102
103    return
104
105def getBasics(ifile, verb=False):
106    """
107    @Description:
108        An example grib file will be opened and basic information will
109        be extracted. These information are important for later use and the
110        initialization of numpy arrays for data storing.
111
112    @Input:
113        ifile: string
114            Contains the full absolute path to the ECMWF grib file.
115        verb (opt): bool
116            Is True if there should be extra output in verbose mode.
117            Default value is False.
118
119    @Return:
120        data: dict
121            Contains basic informations of the ECMWF grib files, e.g.
122            'Ni', 'Nj', 'latitudeOfFirstGridPointInDegrees',
123            'longitudeOfFirstGridPointInDegrees',
124            'latitudeOfLastGridPointInDegrees',
125            'longitudeOfLastGridPointInDegrees',
126            'jDirectionIncrementInDegrees',
127            'iDirectionIncrementInDegrees'
128    """
129    from eccodes import *
130
131    data = {}
132
133    # --- open file ---
134    print("Opening file for getting information data --- %s" %
135          os.path.basename(ifile))
136
137    with open(ifile) as f:
138
139        # load first message from file
140        gid = codes_grib_new_from_file(f)
141
142        # information needed from grib message
143        keys = [
144                'Ni',
145                'Nj',
146                'latitudeOfFirstGridPointInDegrees',
147                'longitudeOfFirstGridPointInDegrees',
148                'latitudeOfLastGridPointInDegrees',
149                'longitudeOfLastGridPointInDegrees',
150                'jDirectionIncrementInDegrees',
151                'iDirectionIncrementInDegrees'
152               ]
153
154        if verb: print('\nInformations are: ')
155        for key in keys:
156            # Get the value of the key in a grib message.
157            data[key] = codes_get(gid,key)
158            if verb: print "%s = %s" % (key,data[key])
159        if verb: print '\n'
160
161        # Free the memory for the message referred as gribid.
162        codes_release(gid)
163
164    return data
165
166def getFilesPerDate(files, datelist):
167    '''
168    @Description:
169        The filenames contain dates which are used to select a list
170        of files for a specific time period specified in datelist.
171
172    @Input:
173        files: instance of UIOFiles
174            For description see class documentation.
175            It contains the attribute "files" which is a list of pathes
176            to filenames.
177
178        datelist: list of datetimes
179            Contains the list of dates which should be processed for plotting.
180
181    @Return:
182        filelist: list of strings
183            Contains the selected files for the time period.
184    '''
185
186    filelist = []
187    for file in files:
188        fdate = file[-8:]
189        ddate = datetime.datetime.strptime(fdate, '%y%m%d%H')
190        if ddate in datelist:
191            filelist.append(file)
192
193    return filelist
194
195def plotRetrieved(args, c):
196    '''
197    @Description:
198        Reads GRIB data from a specified time period, a list of levels
199        and a specified list of parameter.
200
201    @Input:
202        args: instance of ArgumentParser
203            Contains the commandline arguments from script/program call.
204
205        c: instance of class ControlFile
206            Contains all necessary information of a CONTROL file. The parameters
207            are: DAY1, DAY2, DTIME, MAXSTEP, TYPE, TIME, STEP, CLASS, STREAM,
208            NUMBER, EXPVER, GRID, LEFT, LOWER, UPPER, RIGHT, LEVEL, LEVELIST,
209            RESOL, GAUSS, ACCURACY, OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA,
210            SMOOTH, FORMAT, ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS,
211            ECFSDIR, MAILOPS, MAILFAIL, GRIB2FLEXPART, DEBUG, INPUTDIR,
212            OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
213            For more information about format and content of the parameter see
214            documentation.
215
216    @Return:
217        <nothing>
218    '''
219    start = datetime.datetime.strptime(c.start_date, '%Y%m%d%H')
220    end = datetime.datetime.strptime(c.end_date, '%Y%m%d%H')
221
222    # create datelist between start and end date
223    datelist = [start] # initialise datelist with first date
224    run_date = start
225    while run_date < end:
226        run_date += datetime.timedelta(hours=int(c.dtime))
227        datelist.append(run_date)
228
229    print 'datelist: ', datelist
230
231    c.paramIds = asarray(c.paramIds, dtype='int')
232    c.levels = asarray(c.levels, dtype='int')
233    c.area = asarray(c.area)
234
235    # index_keys = ["date", "time", "step"]
236    # indexfile = c.inputdir + "/date_time_stepRange.idx"
237    # silentremove(indexfile)
238    # grib = GribTools(inputfiles.files)
239    # # creates new index file
240    # iid = grib.index(index_keys=index_keys, index_file=indexfile)
241
242    # # read values of index keys
243    # index_vals = []
244    # for key in index_keys:
245        # index_vals.append(grib_index_get(iid, key))
246        # print(index_vals[-1])
247        # # index_vals looks for example like:
248        # # index_vals[0]: ('20171106', '20171107', '20171108') ; date
249        # # index_vals[1]: ('0', '1200', '1800', '600') ; time
250        # # index_vals[2]: ('0', '12', '3', '6', '9') ; stepRange
251
252
253
254
255    #index_keys = ["date", "time", "step", "paramId"]
256    #indexfile = c.inputdir + "/date_time_stepRange.idx"
257    #silentremove(indexfile)
258
259    files = UIOFiles(c.prefix+'*')
260    files.listFiles(c.inputdir)
261    ifiles = getFilesPerDate(files.files, datelist)
262    ifiles.sort()
263
264    gdict = getBasics(ifiles[0], verb=False)
265
266    fdict = dict()
267    fmeta = dict()
268    fstamp = dict()
269    for p in c.paramIds:
270        for l in c.levels:
271            key = '{:0>3}_{:0>3}'.format(p, l)
272            fdict[key] = []
273            fmeta[key] = []
274            fstamp[key] = []
275
276    for file in ifiles:
277        f = open(file)
278        print( "Opening file for reading data --- %s" % file)
279        fdate = datetime.datetime.strptime(file[-8:], "%y%m%d%H")
280
281        # Load in memory a grib message from a file.
282        gid = codes_grib_new_from_file(f)
283        while(gid is not None):
284            gtype = codes_get(gid, 'type')
285            paramId = codes_get(gid, 'paramId')
286            parameterName = codes_get(gid, 'parameterName')
287            level = codes_get(gid, 'level')
288
289            if paramId in c.paramIds and level in c.levels:
290                key = '{:0>3}_{:0>3}'.format(paramId, level)
291                print 'key: ', key
292                if len(fstamp[key]) != 0 :
293                    for i in range(len(fstamp[key])):
294                        if fdate < fstamp[key][i]:
295                            fstamp[key].insert(i, fdate)
296                            fmeta[key].insert(i, [paramId, parameterName, gtype,
297                                                fdate, level])
298                            fdict[key].insert(i, flipud(reshape(
299                                            codes_get_values(gid),
300                                            [gdict['Nj'], gdict['Ni']])))
301                            break
302                        elif fdate > fstamp[key][i] and i == len(fstamp[key])-1:
303                            fstamp[key].append(fdate)
304                            fmeta[key].append([paramId, parameterName, gtype,
305                                                fdate, level])
306                            fdict[key].append(flipud(reshape(
307                                            codes_get_values(gid),
308                                            [gdict['Nj'], gdict['Ni']])))
309                            break
310                        elif fdate > fstamp[key][i] and i != len(fstamp[key])-1 \
311                             and fdate < fstamp[key][i+1]:
312                            fstamp[key].insert(i, fdate)
313                            fmeta[key].insert(i, [paramId, parameterName, gtype,
314                                                    fdate, level])
315                            fdict[key].insert(i, flipud(reshape(
316                                            codes_get_values(gid),
317                                            [gdict['Nj'], gdict['Ni']])))
318                            break
319                        else:
320                            pass
321                else:
322                    fstamp[key].append(fdate)
323                    fmeta[key].append((paramId, parameterName, gtype,
324                                       fdate, level))
325                    fdict[key].append(flipud(reshape(
326                        codes_get_values(gid), [gdict['Nj'], gdict['Ni']])))
327
328            codes_release(gid)
329
330            # Load in memory a grib message from a file.
331            gid = codes_grib_new_from_file(f)
332
333        f.close()
334    #print 'fstamp: ', fstamp
335    #exit()
336
337    for k in fdict.keys():
338        print 'fmeta: ', len(fmeta),fmeta
339        fml = fmeta[k]
340        fdl = fdict[k]
341        print 'fm1: ', len(fml), fml
342        #print 'fd1: ', fdl
343        #print zip(fdl, fml)
344        for fd, fm in zip(fdl, fml):
345            print fm
346            ftitle = fm[1] + ' {} '.format(fm[-1]) + \
347                datetime.datetime.strftime(fm[3], '%Y%m%d%H') #+ ' ' + stats(fd)
348            pname = '_'.join(fm[1].split()) + '_{}_'.format(fm[-1]) + \
349                datetime.datetime.strftime(fm[3], '%Y%m%d%H')
350            plotMap(c, fd, fm, gdict, ftitle, pname, 'png')
351
352    for k in fdict.keys():
353        fml = fmeta[k]
354        fdl = fdict[k]
355        fsl = fstamp[k]
356        if fdl:
357            fm = fml[0]
358            fd = fdl[0]
359            ftitle = fm[1] + ' {} '.format(fm[-1]) + \
360                datetime.datetime.strftime(fm[3], '%Y%m%d%H') #+ ' ' + stats(fd)
361            pname = '_'.join(fm[1].split()) + '_{}_'.format(fm[-1]) + \
362                datetime.datetime.strftime(fm[3], '%Y%m%d%H')
363            lat = -20
364            lon = 20
365            plotTS(c, fdl, fml, fsl, lat, lon,
366                           gdict, ftitle, pname, 'png')
367
368    return
369
370def plotTS(c, flist, fmetalist, ftimestamps, lat, lon,
371                   gdict, ftitle, filename, fending, show=False):
372    '''
373    @Description:
374
375    @Input:
376        c:
377
378        flist:
379            The actual data values to be plotted from the grib messages.
380
381        fmetalist: list of strings
382            Contains some meta date for the data field to be plotted:
383            parameter id, parameter Name, grid type, datetime, level
384
385        ftimestamps: list of datetime
386            Contains the time stamps.
387
388        lat:
389
390        lon:
391
392        gdict:
393
394        ftitle: string
395            The title of the timeseries.
396
397        filename: string
398            The time series is stored in a file with this name.
399
400        fending: string
401            Contains the type of plot, e.g. pdf or png
402
403        show: boolean
404            Decides if the plot is shown after plotting or hidden.
405
406    @Return:
407        <nothing>
408    '''
409    print 'plotting timeseries'
410
411    t1 = time.time()
412
413    llx = gdict['longitudeOfFirstGridPointInDegrees']
414    if llx > 180. :
415        llx -= 360.
416    lly = gdict['latitudeOfLastGridPointInDegrees']
417    dxout = gdict['iDirectionIncrementInDegrees']
418    dyout = gdict['jDirectionIncrementInDegrees']
419    urx = gdict['longitudeOfLastGridPointInDegrees']
420    ury = gdict['latitudeOfFirstGridPointInDegrees']
421    numxgrid = gdict['Ni']
422    numygrid = gdict['Nj']
423
424    farr = asarray(flist)
425    #(time, lat, lon)
426
427    lonindex = linspace(llx, urx, numxgrid)
428    latindex = linspace(lly, ury, numygrid)
429    #print len(lonindex), len(latindex), farr.shape
430
431    #latindex = (lat + 90) * 180 / (gdict['Nj'] - 1)
432    #lonindex = (lon + 179) * 360 / gdict['Ni']
433    #print latindex, lonindex
434
435
436    ts = farr[:, 0, 0]#latindex[0], lonindex[0]]
437
438    fig = plt.figure(figsize=(12, 6.7))
439
440    plt.plot(ftimestamps, ts)
441    plt.title(ftitle)
442
443    plt.savefig(c.outputdir+'/'+filename+'_TS.'+fending, facecolor=fig.get_facecolor(), edgecolor='none',format=fending)
444    print 'created ', c.outputdir + '/' + filename
445    if show == True:
446        plt.show()
447    fig.clf()
448    plt.close(fig)
449
450    print time.time() - t1, 's'
451
452    return
453
454def plotMap(c, flist, fmetalist, gdict, ftitle, filename, fending, show=False):
455    '''
456    @Description:
457
458    @Input:
459        c: instance of class ControlFile
460            Contains all necessary information of a CONTROL file. The parameters
461            are: DAY1, DAY2, DTIME, MAXSTEP, TYPE, TIME, STEP, CLASS, STREAM,
462            NUMBER, EXPVER, GRID, LEFT, LOWER, UPPER, RIGHT, LEVEL, LEVELIST,
463            RESOL, GAUSS, ACCURACY, OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA,
464            SMOOTH, FORMAT, ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS,
465            ECFSDIR, MAILOPS, MAILFAIL, GRIB2FLEXPART, DEBUG, INPUTDIR,
466            OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
467            For more information about format and content of the parameter see
468            documentation.
469
470        flist
471
472        fmetalist
473
474        gdict: dict
475            Contains basic informations of the ECMWF grib files, e.g.
476            'Ni', 'Nj', 'latitudeOfFirstGridPointInDegrees',
477            'longitudeOfFirstGridPointInDegrees',
478            'latitudeOfLastGridPointInDegrees',
479            'longitudeOfLastGridPointInDegrees',
480            'jDirectionIncrementInDegrees',
481            'iDirectionIncrementInDegrees'
482
483        ftitle: string
484            The titel of the plot.
485
486        filename: string
487            The plot is stored in a file with this name.
488
489        fending: string
490            Contains the type of plot, e.g. pdf or png
491
492        show: boolean
493            Decides if the plot is shown after plotting or hidden.
494
495    @Return:
496        <nothing>
497    '''
498    print 'plotting map'
499
500    t1 = time.time()
501
502    fig = plt.figure(figsize=(12, 6.7))
503    #mbaxes = fig.add_axes([0.05, 0.15, 0.8, 0.7])
504
505    llx = gdict['longitudeOfFirstGridPointInDegrees'] #- 360
506    if llx > 180. :
507        llx -= 360.
508    lly = gdict['latitudeOfLastGridPointInDegrees']
509    dxout = gdict['iDirectionIncrementInDegrees']
510    dyout = gdict['jDirectionIncrementInDegrees']
511    urx = gdict['longitudeOfLastGridPointInDegrees']
512    ury = gdict['latitudeOfFirstGridPointInDegrees']
513    numxgrid = gdict['Ni']
514    numygrid = gdict['Nj']
515
516    m = Basemap(projection='cyl', llcrnrlon=llx, llcrnrlat=lly,
517                urcrnrlon=urx, urcrnrlat=ury,resolution='i')
518
519    lw = 0.5
520    m.drawmapboundary()
521    x = linspace(llx, urx, numxgrid)
522    y = linspace(lly, ury, numygrid)
523
524    xx, yy = m(*meshgrid(x, y))
525
526    #s = m.contourf(xx, yy, flist)
527
528    s = plt.imshow(flist.T,
529                    extent=(llx, urx, lly, ury),
530                    alpha=1.0,
531                    interpolation='nearest'
532                    #vmin=vn,
533                    #vmax=vx,
534                    #cmap=my_cmap,
535                    #levels=levels,
536                    #cmap=my_cmap,
537                    #norm=LogNorm(vn,vx)
538                    )
539
540    title(ftitle, y=1.08)
541    cb = m.colorbar(s, location="right", pad="10%")
542    #cb.set_label('Contribution per cell (ng m$^{-3}$)',size=14)
543
544    thickline = np.arange(lly,ury+1,10.)
545    thinline  = np.arange(lly,ury+1,5.)
546    m.drawparallels(thickline,color='gray',dashes=[1,1],linewidth=0.5,labels=[1,1,1,1], xoffset=1.) # draw parallels
547    m.drawparallels(np.setdiff1d(thinline,thickline),color='lightgray',dashes=[1,1],linewidth=0.5,labels=[0,0,0,0]) # draw parallels
548
549    thickline = np.arange(llx,urx+1,10.)
550    thinline  = np.arange(llx,urx+1,5.)
551    m.drawmeridians(thickline,color='gray',dashes=[1,1],linewidth=0.5,labels=[1,1,1,1],yoffset=1.) # draw meridians
552    m.drawmeridians(np.setdiff1d(thinline,thickline),color='lightgray',dashes=[1,1],linewidth=0.5,labels=[0,0,0,0]) # draw meridians
553
554    m.drawcoastlines()
555    m.drawcountries()
556
557    plt.savefig(c.outputdir+'/'+filename+'_MAP.'+fending, facecolor=fig.get_facecolor(), edgecolor='none',format=fending)
558    print 'created ', c.outputdir + '/' + filename
559    if show == True:
560        plt.show()
561    fig.clf()
562    plt.close(fig)
563
564    print time.time() - t1, 's'
565
566    return
567
568def getPlotArgs():
569    '''
570    @Description:
571        Assigns the command line arguments and reads CONTROL file
572        content. Apply default values for non mentioned arguments.
573
574    @Input:
575        <nothing>
576
577    @Return:
578        args: instance of ArgumentParser
579            Contains the commandline arguments from script/program call.
580
581        c: instance of class ControlFile
582            Contains all necessary information of a CONTROL file. The parameters
583            are: DAY1, DAY2, DTIME, MAXSTEP, TYPE, TIME, STEP, CLASS, STREAM,
584            NUMBER, EXPVER, GRID, LEFT, LOWER, UPPER, RIGHT, LEVEL, LEVELIST,
585            RESOL, GAUSS, ACCURACY, OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA,
586            SMOOTH, FORMAT, ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS,
587            ECFSDIR, MAILOPS, MAILFAIL, GRIB2FLEXPART, DEBUG, INPUTDIR,
588            OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
589            For more information about format and content of the parameter see
590            documentation.
591    '''
592    parser = ArgumentParser(description='Plot retrieved GRIB data from ' + \
593                            'ECMWF MARS archive',
594                            formatter_class=ArgumentDefaultsHelpFormatter)
595
596# the most important arguments
597    parser.add_argument("--start_date", dest="start_date",
598                        help="start date YYYYMMDD")
599    parser.add_argument( "--end_date", dest="end_date",
600                         help="end_date YYYYMMDD")
601
602    parser.add_argument("--start_step", dest="start_step",
603                        help="start step in hours")
604    parser.add_argument( "--end_step", dest="end_step",
605                         help="end step in hours")
606
607# some arguments that override the default in the CONTROL file
608    parser.add_argument("--levelist", dest="levelist",
609                        help="vertical levels to be retrieved, e.g. 30/to/60")
610    parser.add_argument("--area", dest="area",
611                        help="area defined as north/west/south/east")
612    parser.add_argument("--paramIds", dest="paramIds",
613                        help="parameter IDs")
614    parser.add_argument("--prefix", dest="prefix", default='EN',
615                        help="output file name prefix")
616
617# set the working directories
618    parser.add_argument("--inputdir", dest="inputdir", default=None,
619                        help="root directory for storing intermediate files")
620    parser.add_argument("--outputdir", dest="outputdir", default=None,
621                        help="root directory for storing output files")
622    parser.add_argument("--flexpart_root_scripts", dest="flexpart_root_scripts",
623                        help="FLEXPART root directory (to find \
624                        'grib2flexpart and COMMAND file)\n \
625                        Normally ECMWFDATA resides in the scripts directory \
626                        of the FLEXPART distribution")
627
628    parser.add_argument("--controlfile", dest="controlfile",
629                        default='CONTROL.temp',
630                        help="file with CONTROL parameters")
631    args = parser.parse_args()
632
633    try:
634        c = ControlFile(args.controlfile)
635    except IOError:
636        try:
637            c = ControlFile(localpythonpath + args.controlfile)
638
639        except:
640            print 'Could not read CONTROL file "' + args.controlfile + '"'
641            print 'Either it does not exist or its syntax is wrong.'
642            print 'Try "' + sys.argv[0].split('/')[-1] + \
643                  ' -h" to print usage information'
644            exit(1)
645
646    if args.levelist:
647        c.levels = args.levelist.split('/')
648    else:
649        c.levels = [0]
650
651    if args.area:
652        c.area = args.area.split('/')
653    else:
654        c.area = '[0,0]'
655
656    c.paramIds = args.paramIds.split('/')
657
658    if args.start_step:
659        c.start_step = int(args.start_step)
660    else:
661        c.start_step = 0
662
663    if args.end_step:
664        c.end_step = int(args.end_step)
665    else:
666        c.end_step = 0
667
668    c.start_date = args.start_date
669    c.end_date = args.end_date
670
671    c.prefix = args.prefix
672
673    c.inputdir = args.inputdir
674
675    if args.outputdir:
676        c.outputdir = args.outputdir
677    else:
678        c.outputdir = c.inputdir
679
680    return args, c
681
682if __name__ == "__main__":
683    main()
684
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG