source: flex_extract.git/python/FlexpartTools.py @ 02c8c50

ctbtodev
Last change on this file since 02c8c50 was 02c8c50, checked in by Anne Philipp <bscannephilipp@…>, 6 years ago

more changes in PEP8 style and slight modifications in coding style and naming. More documentation of functions.

  • Property mode set to 100644
File size: 94.3 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#************************************************************************
4# TODO AP
5#AP
6# - Functionality Description Provided is not correct for this file
7# - localpythonpath should not be set in module load section!
8# - Change History ist nicht angepasst ans File! Original geben lassen
9# - def myerror muss angepasst werden da derzeit manuelle modifikation notwendig
10# - the gaussian keyword in mars retrieval should be removed!!! but kept in
11#   control file for the reason of calculation of eta dot from gaussian grid
12# - call of convert in eigene Funktion auslagern
13
14#************************************************************************
15"""
16@Author: Anne Fouilloux (University of Oslo)
17
18@Date: October 2014
19
20@ChangeHistory:
21    November 2015 - Leopold Haimberger (University of Vienna):
22        - using the WebAPI also for general MARS retrievals
23        - job submission on ecgate and cca
24        - job templates suitable for twice daily operational dissemination
25        - dividing retrievals of longer periods into digestable chunks
26        - retrieve also longer term forecasts, not only analyses and
27          short term forecast data
28        - conversion into GRIB2
29        - conversion into .fp format for faster execution of FLEXPART
30
31    February 2018 - Anne Philipp (University of Vienna):
32        - applied PEP8 style guide
33        - added documentation
34
35@License:
36    (C) Copyright 2014 UIO.
37
38    This software is licensed under the terms of the Apache Licence Version 2.0
39    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
40
41@Requirements:
42    - A standard python 2.6 or 2.7 installation
43    - dateutils
44    - matplotlib (optional, for debugging)
45    - ECMWF specific packages, all available from https://software.ecmwf.int/
46        ECMWF WebMARS, gribAPI with python enabled, emoslib and
47        ecaccess web toolkit
48
49@Description:
50    Further documentation may be obtained from www.flexpart.eu.
51
52    Functionality provided:
53        Prepare input 3D-wind fields in hybrid coordinates +
54        surface fields for FLEXPART runs
55"""
56# ------------------------------------------------------------------------------
57# MODULES
58# ------------------------------------------------------------------------------
59import subprocess
60from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
61import traceback
62import shutil
63import os
64import errno
65import sys
66import inspect
67import glob
68import datetime
69from string import atoi
70from numpy import *
71ecapi = True
72try:
73    import ecmwfapi
74except ImportError:
75    ecapi = False
76from gribapi import *
77from GribTools import GribTools
78
79def interpret_args_and_control():
80    '''
81    @Description:
82        Assigns the command line arguments and reads control file
83        content. Apply default values for non mentioned arguments.
84
85    @Input:
86        <nothing>
87
88    @Return:
89        args: instance of ArgumentParser
90            Contains the commandline arguments from script/program call.
91
92        c: instance of class Control
93            Contains all necessary information of a control file. The parameters
94            are: DAY1, DAY2, DTIME, MAXSTEP, TYPE, TIME, STEP, CLASS, STREAM,
95            NUMBER, EXPVER, GRID, LEFT, LOWER, UPPER, RIGHT, LEVEL, LEVELIST,
96            RESOL, GAUSS, ACCURACY, OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA,
97            SMOOTH, FORMAT, ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS,
98            ECFSDIR, MAILOPS, MAILFAIL, GRIB2FLEXPART, DEBUG, INPUTDIR,
99            OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
100            For more information about format and content of the parameter see
101            documentation.
102
103    '''
104    parser = ArgumentParser(description='Retrieve FLEXPART input from \
105                            ECMWF MARS archive',
106                            formatter_class=ArgumentDefaultsHelpFormatter)
107
108    # the most important arguments
109    parser.add_argument("--start_date", dest="start_date",
110                        help="start date YYYYMMDD")
111    parser.add_argument("--end_date", dest="end_date",
112                        help="end_date YYYYMMDD")
113    parser.add_argument("--date_chunk", dest="date_chunk", default=None,
114                        help="# of days to be retrieved at once")
115
116    # some arguments that override the default in the control file
117    parser.add_argument("--basetime", dest="basetime",
118                        help="base such as 00/12 (for half day retrievals)")
119    parser.add_argument("--step", dest="step",
120                        help="steps such as 00/to/48")
121    parser.add_argument("--levelist", dest="levelist",
122                        help="Vertical levels to be retrieved, e.g. 30/to/60")
123    parser.add_argument("--area", dest="area",
124                        help="area defined as north/west/south/east")
125
126    # set the working directories
127    parser.add_argument("--inputdir", dest="inputdir", default=None,
128                        help="root directory for storing intermediate files")
129    parser.add_argument("--outputdir", dest="outputdir", default=None,
130                        help="root directory for storing output files")
131    parser.add_argument("--flexpart_root_scripts", dest="flexpart_root_scripts",
132                        help="FLEXPART root directory (to find grib2flexpart \
133                        and COMMAND file)\n\ Normally ECMWFDATA resides in \
134                        the scripts directory of the FLEXPART distribution")
135
136    # this is only used by prepareFLEXPART.py to rerun a postprocessing step
137    parser.add_argument("--ppid", dest="ppid",
138                        help="Specify parent process id for \
139                        rerun of prepareFLEXPART")
140
141    # arguments for job submission to ECMWF, only needed by submit.py
142    parser.add_argument("--job_template", dest='job_template',
143                        default="job.temp",
144                        help="job template file for submission to ECMWF")
145    parser.add_argument("--queue", dest="queue",
146                        help="queue for submission to ECMWF \
147                        (e.g. ecgate or cca )")
148    parser.add_argument("--controlfile", dest="controlfile",
149                        default='CONTROL.temp',
150                        help="file with control parameters")
151    parser.add_argument("--debug", dest="debug", default=0,
152                        help="Debug mode - leave temporary files intact")
153
154    args = parser.parse_args()
155
156    # create instance of Control for specified controlfile
157    # and assign the parameters (and default values if necessary)
158    try:
159        c = Control(args.controlfile)
160    except IOError:
161        try:
162            c = Control(localpythonpath + args.controlfile)
163        except:
164            print('Could not read control file "' + args.controlfile + '"')
165            print('Either it does not exist or its syntax is wrong.')
166            print('Try "' + sys.argv[0].split('/')[-1] +
167                  ' -h" to print usage information')
168            exit(1)
169
170    # check for having at least a starting date
171    if  args.start_date is None and getattr(c, 'start_date') is None:
172        print('start_date specified neither in command line nor \
173               in control file ' + args.controlfile)
174        print('Try "' + sys.argv[0].split('/')[-1] +
175              ' -h" to print usage information')
176        exit(1)
177
178    # save all existing command line parameter to the Control instance
179    # if parameter is not specified through the command line or CONTROL file
180    # set default values
181    if args.start_date is not None:
182        c.start_date = args.start_date
183    if args.end_date is not None:
184        c.end_date = args.end_date
185    if c.end_date is None:
186        c.end_date = c.start_date
187    if args.date_chunk is not None:
188        c.date_chunk = args.date_chunk
189
190    if not hasattr(c, 'debug'):
191        c.debug = args.debug
192
193    if args.inputdir is None and args.outputdir is None:
194        c.inputdir = '../work'
195        c.outputdir = '../work'
196    else:
197        if args.inputdir is not None:
198            c.inputdir = args.inputdir
199        if args.outputdir is None:
200            c.outputdir = args.inputdir
201        if args.outputdir is not None:
202            c.outputdir = args.outputdir
203        if args.inputdir is None:
204            c.inputdir = args.outputdir
205
206    if hasattr(c, 'outputdir') is False and args.outputdir is None:
207        c.outputdir = c.inputdir
208    else:
209        if args.outputdir is not None:
210            c.outputdir = args.outputdir
211
212    if args.area is not None:
213        afloat = '.' in args.area
214        l = args.area.split('/')
215        if afloat:
216            for i in range(len(l)):
217                l[i] = str(int(float(l[i]) * 1000))
218        c.upper, c.left, c.lower, c.right = l
219
220    # NOTE: basetime activates the ''operational mode''
221    if args.basetime is not None:
222        c.basetime = args.basetime
223
224    if args.step is not None:
225        l = args.step.split('/')
226        if 'to' in args.step.lower():
227            if 'by' in args.step.lower():
228                ilist = arange(int(l[0]), int(l[2]) + 1, int(l[4]))
229                c.step = ['{:0>3}'.format(i) for i in ilist]
230            else:
231                myerror(None, args.step + ':\n' +
232                        'please use "by" as well if "to" is used')
233        else:
234            c.step = l
235
236    if args.levelist is not None:
237        c.levelist = args.levelist
238        if 'to' in c.levelist:
239            c.level = c.levelist.split('/')[2]
240        else:
241            c.level = c.levelist.split('/')[-1]
242
243    if args.flexpart_root_scripts is not None:
244        c.flexpart_root_scripts = args.flexpart_root_scripts
245
246    return args, c
247
248
249def install_args_and_control():
250    '''
251    @Description:
252        Assigns the command line arguments for installation and reads
253        control file content. Apply default values for non mentioned arguments.
254
255    @Input:
256        <nothing>
257
258    @Return:
259        args: instance of ArgumentParser
260            Contains the commandline arguments from script/program call.
261
262        c: instance of class Control
263            Contains all necessary information of a control file. The parameters
264            are: DAY1, DAY2, DTIME, MAXSTEP, TYPE, TIME, STEP, CLASS, STREAM,
265            NUMBER, EXPVER, GRID, LEFT, LOWER, UPPER, RIGHT, LEVEL, LEVELIST,
266            RESOL, GAUSS, ACCURACY, OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA,
267            SMOOTH, FORMAT, ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS,
268            ECFSDIR, MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR
269            For more information about format and content of the parameter see
270            documentation.
271    '''
272    parser = ArgumentParser(description='Install ECMWFDATA software locally or \
273                            on ECMWF machines',
274                            formatter_class=ArgumentDefaultsHelpFormatter)
275
276    parser.add_argument('--target', dest='install_target',
277                        help="Valid targets: local | ecgate | cca , \
278                        the latter two are at ECMWF")
279    parser.add_argument("--makefile", dest="makefile",
280                        help='Name of Makefile to use for compiling CONVERT2')
281    parser.add_argument("--ecuid", dest="ecuid",
282                        help='user id at ECMWF')
283    parser.add_argument("--ecgid", dest="ecgid",
284                        help='group id at ECMWF')
285    parser.add_argument("--gateway", dest="gateway",
286                        help='name of local gateway server')
287    parser.add_argument("--destination", dest="destination",
288                        help='ecaccess destination, e.g. leo@genericSftp')
289
290    parser.add_argument("--flexpart_root_scripts", dest="flexpart_root_scripts",
291                        help="FLEXPART root directory on ECMWF servers \
292                        (to find grib2flexpart and COMMAND file)\n\
293                        Normally ECMWFDATA resides in the scripts directory \
294                        of the FLEXPART distribution, thus the:")
295
296# arguments for job submission to ECMWF, only needed by submit.py
297    parser.add_argument("--job_template", dest='job_template',
298                        default="job.temp.o",
299                        help="job template file for submission to ECMWF")
300
301    parser.add_argument("--controlfile", dest="controlfile",
302                        default='CONTROL.temp',
303                        help="file with control parameters")
304
305    args = parser.parse_args()
306
307    try:
308        c = Control(args.controlfile)
309    except:
310        print('Could not read control file "' + args.controlfile + '"')
311        print('Either it does not exist or its syntax is wrong.')
312        print('Try "' + sys.argv[0].split('/')[-1] +
313              ' -h" to print usage information')
314        exit(1)
315
316    if args.install_target != 'local':
317        if (args.ecgid is None or args.ecuid is None or args.gateway is None
318            or args.destination is None):
319            print('Please enter your ECMWF user id and group id as well as \
320                   the \nname of the local gateway and the ectrans \
321                   destination ')
322            print('with command line options --ecuid --ecgid \
323                   --gateway --destination')
324            print('Try "' + sys.argv[0].split('/')[-1] +
325                  ' -h" to print usage information')
326            print('Please consult ecaccess documentation or ECMWF user support \
327                   for further details')
328            sys.exit(1)
329        else:
330            c.ecuid = args.ecuid
331            c.ecgid = args.ecgid
332            c.gateway = args.gateway
333            c.destination = args.destination
334
335    try:
336        c.makefile = args.makefile
337    except:
338        pass
339
340    if args.install_target == 'local':
341        if args.flexpart_root_scripts is None:
342            c.flexpart_root_scripts = '../'
343        else:
344            c.flexpart_root_scripts = args.flexpart_root_scripts
345
346    if args.install_target != 'local':
347        if args.flexpart_root_scripts is None:
348            c.ec_flexpart_root_scripts = '${HOME}'
349        else:
350            c.ec_flexpart_root_scripts = args.flexpart_root_scripts
351
352    return args, c
353
354
355def cleanup(c):
356    '''
357    @Description:
358        Remove all files from intermediate directory
359        (inputdir from control file).
360
361    @Input:
362        c: instance of class Control
363            Contains all the parameters of control files, which are e.g.:
364            DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
365            STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
366            LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
367            OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
368            ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
369            MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR, BASETIME
370            DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
371
372            For more information about format and content of the parameter
373            see documentation.
374
375    @Return:
376        <nothing>
377    '''
378
379    print("cleanup")
380
381    cleanlist = glob.glob(c.inputdir + "/*")
382    for cl in cleanlist:
383        if c.prefix not in cl:
384            silentremove(cl)
385        if c.ecapi is False and (c.ectrans == '1' or c.ecstorage == '1'):
386            silentremove(cl)
387
388    print("Done")
389
390    return
391
392
393def myerror(c, message='ERROR'):
394    '''
395    @Description:
396        Prints a specified error message which can be passed to the function
397        before exiting the program.
398
399    @Input:
400        c: instance of class Control
401            Contains all the parameters of control files, which are e.g.:
402            DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
403            STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
404            LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
405            OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
406            ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
407            MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR, BASETIME
408            DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
409
410            For more information about format and content of the parameter
411            see documentation.
412
413        message: string, optional
414            Error message. Default value is "ERROR".
415
416    @Return:
417        <nothing>
418    '''
419    # uncomment if user wants email notification directly from python
420    #try:
421        #target = c.mailfail
422    #except AttributeError:
423        #target = os.getenv('USER')
424
425    #if(type(target) is not list):
426        #target = [target]
427
428    print(message)
429
430    # uncomment if user wants email notification directly from python
431    #for t in target:
432    #p = subprocess.Popen(['mail','-s ECMWFDATA v7.0 ERROR', os.path.expandvars(t)],
433    #                     stdin = subprocess.PIPE, stdout = subprocess.PIPE,
434    #                     stderr = subprocess.PIPE, bufsize = 1)
435    #tr = '\n'.join(traceback.format_stack())
436    #pout = p.communicate(input = message+'\n\n'+tr)[0]
437    #print 'Email sent to '+os.path.expandvars(t) # +' '+pout.decode()
438
439    exit(1)
440
441    return
442
443
444def normalexit(c, message='Done!'):
445    '''
446    @Description:
447        Prints a specific exit message which can be passed to the function.
448
449    @Input:
450        c: instance of class Control
451            Contains all the parameters of control files, which are e.g.:
452            DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
453            STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
454            LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
455            OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
456            ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
457            MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR, BASETIME
458            DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
459
460            For more information about format and content of the parameter
461            see documentation.
462
463        message: string, optional
464            Message for exiting program. Default value is "Done!".
465
466    @Return:
467        <nothing>
468
469    '''
470    # Uncomment if user wants notification directly from python
471    #try:
472        #target = c.mailops
473        #if(type(target) is not list):
474            #target = [target]
475        #for t in target:
476            #p = subprocess.Popen(['mail','-s ECMWFDATA v7.0 normal exit',
477            #                      os.path.expandvars(t)],
478            #                      stdin = subprocess.PIPE,
479            #                      stdout = subprocess.PIPE,
480            #                      stderr = subprocess.PIPE, bufsize = 1)
481            #pout = p.communicate(input = message+'\n\n')[0]
482            #print pout.decode()
483    #except:
484        #pass
485
486    print(message)
487
488    return
489
490
491def product(*args, **kwds):
492    '''
493    @Description:
494        This method is taken from an example at the ECMWF wiki website.
495        https://software.ecmwf.int/wiki/display/GRIB/index.py; 2018-03-16
496
497        This method combines the single characters of the passed arguments
498        with each other. So that each character of each argument value
499        will be combined with each character of the other arguments as a tuple.
500
501        Example:
502        product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
503        product(range(2), repeat = 3) --> 000 001 010 011 100 101 110 111
504
505    @Input:
506        *args: tuple
507            Positional arguments (arbitrary number).
508
509        **kwds: dictionary
510            Contains all the keyword arguments from *args.
511
512    @Return:
513        prod: tuple
514            Return will be done with "yield". A tuple of combined arguments.
515            See example in description above.
516    '''
517
518    pools = map(tuple, args) * kwds.get('repeat', 1)
519    result = [[]]
520    for pool in pools:
521        result = [x + [y] for x in result for y in pool]
522    for prod in result:
523        yield tuple(prod)
524
525    return
526
527
528def silentremove(filename):
529    '''
530    @Description:
531        Removes the file which name is passed to the function if
532        it exists. The function does not fail if the file does not
533        exist.
534
535    @Input:
536        filename: string
537            The name of the file to be removed without notification.
538
539    @Return:
540        <nothing>
541    '''
542    try:
543        os.remove(filename)
544    except OSError as e:
545        # this would be "except OSError, e:" before Python 2.6
546        if e.errno is not  errno.ENOENT:
547            # errno.ENOENT  =  no such file or directory
548            raise  # re-raise exception if a different error occured
549
550    return
551
552
553def init128(fn):
554    '''
555    @Description:
556        Opens and reads the grib file with table 128 information.
557
558    @Input:
559        fn: string
560            Path to file of ECMWF grib table number 128.
561
562    @Return:
563        table128: dictionary
564            Contains the ECMWF grib table 128 information.
565    '''
566    table128 = dict()
567    with open(fn) as f:
568        fdata = f.read().split('\n')
569    for data in fdata:
570        if data[0] != '!':
571            table128[data[0:3]] = data[59:64].strip()
572
573    return table128
574
575
576def toparamId(pars, table):
577    '''
578    @Description:
579        Transform parameter names to parameter ids
580        with ECMWF grib table 128.
581
582    @Input:
583        pars: string
584            Addpar argument from control file in the format of
585            parameter names instead of ids.
586
587        table: dictionary
588            Contains the ECMWF grib table 128 information.
589
590    @Return:
591        ipar: list of integer
592            List of addpar parameters from control file transformed to
593            parameter ids in the format of integer.
594    '''
595    cpar = pars.upper().split('/')
596    ipar = []
597    for par in cpar:
598        found = False
599        for k, v in table.iteritems():
600            if par == k or par == v:
601                ipar.append(int(k))
602                found = True
603                break
604        if found is False:
605            print('Warning: par ' + par + ' not found in table 128')
606
607    return ipar
608
609
610def dapoly(alist):
611    '''
612    @Description:
613        Interpolation of deaccumulated fluxes of an ECMWF model FG field
614        using a cubic polynomial solution which conserves the integrals
615        of the fluxes within each timespan.
616        Disaggregation is done for a list of 4 values to generate a new
617        interpolated value which is output at the central point of the 4
618        accumulation timespans.
619
620    @Input:
621        alist: list of size 4, float
622            List of 4 flux values.
623
624    @Return:
625        nvalue: float
626            New value for the second position of the disaggregated
627            fluxes field.
628
629    '''
630    pya = (alist[3] - alist[0] + 3. * (alist[1] - alist[2])) / 6.
631    pyb = (alist[2] + alist[0]) / 2. - alist[1] - 9. * pya / 2.
632    pyc = alist[1] - alist[0] - 7. * pya / 2. - 2. * pyb
633    pyd = alist[0] - pya / 4. - pyb / 3. - pyc / 2.
634    nvalue = 8. * pya + 4. * pyb + 2. * pyc + pyd
635
636    return nvalue
637
638
639def darain(alist):
640    '''
641    @Description:
642        Interpolation of deaccumulated precipitation fiels of the ECMWF fields
643        using a modified linear solution.
644        Disaggregate a list of 4 precipitation values to generate a new value
645        for the second position of the 4 value list. The interpolated values
646        are output at the central point of the 4 accumulation timespans
647        This is used for precipitation fields.
648
649    @Input:
650        alist: list of size 4, float
651            List of 4 precipitation values.
652
653    @Return:
654        nvalue: float
655            New value for the second position of the disaggregated
656            precipitation field.
657    '''
658    xa = alist[0]
659    xb = alist[1]
660    xc = alist[2]
661    xd = alist[3]
662    xa[xa < 0.] = 0.
663    xb[xb < 0.] = 0.
664    xc[xc < 0.] = 0.
665    xd[xd < 0.] = 0.
666
667    xac = 0.5 * xb
668    mask = xa + xc > 0.
669    xac[mask] = xb[mask] * xc[mask] / (xa[mask] + xc[mask])
670    xbd = 0.5 * xc
671    mask = xb + xd > 0.
672    xbd[mask] = xb[mask] * xc[mask] / (xb[mask] + xd[mask])
673    nvalue = xac + xbd
674
675    return nvalue
676
677
678class Control:
679    '''
680    Class containing the information of the ECMWFDATA control file.
681
682    Contains all the parameters of control files, which are e.g.:
683    DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
684    STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
685    LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
686    OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
687    ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
688    MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR,
689    BASETIME, DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
690
691    For more information about format and content of the parameter
692    see documentation.
693
694    '''
695
696    def __init__(self, filename):
697        '''
698        @Description:
699            Initialises the instance of Control class and defines and
700            assign all controlfile variables. Set default values if
701            parameter was not in CONTROL file.
702
703        @Input:
704            self: instance of Control class
705                Description see class documentation.
706
707            filename: string
708                Name of control file.
709
710        @Return:
711            <nothing>
712        '''
713
714        # read whole CONTROL file
715        with open(filename) as f:
716            fdata = f.read().split('\n')
717
718        # go through every line and store parameter
719        # as class variable
720        for ldata in fdata:
721            data = ldata.split()
722            if len(data) > 1:
723                if 'm_' in data[0].lower():
724                    data[0] = data[0][2:]
725                if data[0].lower() == 'class':
726                    data[0] = 'marsclass'
727                if data[0].lower() == 'day1':
728                    data[0] = 'start_date'
729                if data[0].lower() == 'day2':
730                    data[0] = 'end_date'
731                if data[0].lower() == 'addpar':
732                    if '/' in data[1]:
733                        # remove leading '/' sign from addpar content
734                        if data[1][0] == '/':
735                            data[1] = data[1][1:]
736                        dd = data[1].split('/')
737                        data = [data[0]]
738                        for d in dd:
739                            data.append(d)
740                    pass
741                if len(data) == 2:
742                    if '$' in data[1]:
743                        setattr(self, data[0].lower(), data[1])
744                        while '$' in data[1]:
745                            i = data[1].index('$')
746                            j = data[1].find('{')
747                            k = data[1].find('}')
748                            var = os.getenv(data[1][j+1:k])
749                            if var is not None:
750                                data[1] = data[1][:i] + var + data[1][k+1:]
751                            else:
752                                myerror(None, 'Could not find variable ' +
753                                        data[1][j+1:k] + ' while reading ' +
754                                        filename)
755                        setattr(self, data[0].lower()+'_expanded', data[1])
756                    else:
757                        if data[1].lower() != 'none':
758                            setattr(self, data[0].lower(), data[1])
759                        else:
760                            setattr(self, data[0].lower(), None)
761                elif len(data) > 2:
762                    setattr(self, data[0].lower(), (data[1:]))
763            else:
764                pass
765
766        # check a couple of necessary attributes if they contain values
767        # otherwise set default values
768        if not hasattr(self, 'start_date'):
769            self.start_date = None
770        if not hasattr(self, 'end_date'):
771            self.end_date = self.start_date
772        if not hasattr(self, 'accuracy'):
773            self.accuracy = 24
774        if not hasattr(self, 'omega'):
775            self.omega = '0'
776        if not hasattr(self, 'cwc'):
777            self.cwc = '0'
778        if not hasattr(self, 'omegadiff'):
779            self.omegadiff = '0'
780        if not hasattr(self, 'etadiff'):
781            self.etadiff = '0'
782        if not hasattr(self, 'levelist'):
783            if not hasattr(self, 'level'):
784                print(('Warning: neither levelist nor level \
785                       specified in CONTROL file'))
786            else:
787                self.levelist = '1/to/' + self.level
788        else:
789            if 'to' in self.levelist:
790                self.level = self.levelist.split('/')[2]
791            else:
792                self.level = self.levelist.split('/')[-1]
793
794        if not hasattr(self, 'maxstep'):
795            # find out maximum step
796            self.maxstep = 0
797            for s in self.step:
798                if int(s) > self.maxstep:
799                    self.maxstep = int(s)
800        else:
801            self.maxstep = int(self.maxstep)
802
803        if not hasattr(self, 'prefix'):
804            self.prefix = 'EN'
805        if not hasattr(self, 'makefile'):
806            self.makefile = None
807        if not hasattr(self, 'basetime'):
808            self.basetime = None
809        if not hasattr(self, 'date_chunk'):
810            self.date_chunk = '3'
811        if not hasattr(self, 'grib2flexpart'):
812            self.grib2flexpart = '0'
813
814        # script directory
815        self.ecmwfdatadir = os.path.dirname(os.path.abspath(
816                                            inspect.getfile(
817                                            inspect.currentframe()))) + '/../'
818        # Fortran source directory
819        self.exedir = self.ecmwfdatadir + 'src/'
820
821        # FLEXPART directory
822        if not hasattr(self, 'flexpart_root_scripts'):
823            self.flexpart_root_scripts = self.ecmwfdatadir
824
825        return
826
827
828    def __str__(self):
829        '''
830        @Description:
831            Prepares a single string with all the comma seperated Control
832            class attributes including their values.
833
834            Example:
835            {'kids': 0, 'name': 'Dog', 'color': 'Spotted',
836             'age': 10, 'legs': 2, 'smell': 'Alot'}
837
838        @Input:
839            self: instance of Control class
840                Description see class documentation.
841
842        @Return:
843            string of Control class attributes with their values
844        '''
845
846        attrs = vars(self)
847
848        return ', '.join("%s: %s" % item for item in attrs.items())
849
850    def tolist(self):
851        '''
852        @Description:
853            Just generates a list of strings containing the attributes and
854            assigned values except the attributes "_expanded", "exedir",
855            "ecmwfdatadir" and "flexpart_root_scripts".
856
857        @Input:
858            self: instance of Control class
859                Description see class documentation.
860
861        @Return:
862            l: list
863                A sorted list of the all Control class attributes with
864                their values except the attributes "_expanded", "exedir",
865                "ecmwfdatadir" and "flexpart_root_scripts".
866        '''
867        attrs = vars(self)
868        l = list()
869        for item in attrs.items():
870            if '_expanded' in item[0]:
871                pass
872            elif 'exedir' in item[0]:
873                pass
874            elif 'flexpart_root_scripts' in item[0]:
875                pass
876            elif 'ecmwfdatadir' in item[0]:
877                pass
878            else:
879                if type(item[1]) is list:
880                    stot = ''
881                    for s in item[1]:
882                        stot += s + ' '
883
884                    l.append("%s %s" % (item[0], stot))
885                else:
886#AP syntax error with doubled %s ???
887                    l.append("%s %s" % item )
888        return sorted(l)
889
890
891class MARSretrieval:
892    '''
893    Class for submitting MARS retrievals.
894
895    A description of MARS keywords/arguments and examples of their
896    values can be found here:
897    https://software.ecmwf.int/wiki/display/UDOC/\
898                   Identification+keywords#Identificationkeywords-class
899
900    '''
901
902    def __init__(self, server, marsclass = "ei", type = "", levtype = "",
903                 levelist = "", repres = "", date = "", resol = "", stream = "",
904                 area = "", time = "", step = "", expver = "1", number = "",
905                 accuracy = "", grid = "", gaussian = "", target = "",
906                 param = ""):
907        '''
908        @Description:
909            Initialises the instance of the MARSretrieval class and
910            defines and assigns a set of the necessary retrieval parameters
911            for the FLEXPART input data.
912            A description of MARS keywords/arguments, their dependencies
913            on each other and examples of their values can be found here:
914
915            https://software.ecmwf.int/wiki/display/UDOC/MARS+keywords
916
917        @Input:
918            self: instance of MARSretrieval
919                For description see class documentation.
920
921            server: instance of ECMWFService (from ECMWF Web-API)
922                This is the connection to the ECMWF data servers.
923                It is needed for the pythonic access of ECMWF data.
924
925            marsclass: string, optional
926                Characterisation of dataset. E.g. EI (ERA-Interim),
927                E4 (ERA40), OD (Operational archive), ea (ERA5).
928                Default is the ERA-Interim dataset "ei".
929
930            type: string, optional
931                Determines the type of fields to be retrieved.
932                Selects between observations, images or fields.
933                Examples for fields: Analysis (an), Forecast (fc),
934                Perturbed Forecast (pf), Control Forecast (cf) and so on.
935                Default is an empty string.
936
937            levtype: string, optional
938                Denotes type of level. Has a direct implication on valid
939                levelist values!
940                E.g. model level (ml), pressure level (pl), surface (sfc),
941                potential vorticity (pv), potential temperature (pt)
942                and depth (dp).
943                Default is an empty string.
944
945            levelist: string, optional
946                Specifies the required levels. It has to have a valid
947                correspondence to the selected levtype.
948                Examples: model level: 1/to/137, pressure levels: 500/to/1000
949                Default is an empty string.
950
951            repres: string, optional
952                Selects the representation of the archived data.
953                E.g. sh - spherical harmonics, gg - Gaussian grid,
954                ll - latitude/longitude, ...
955                Default is an empty string.
956
957            date: string, optional
958                Specifies the Analysis date, the Forecast base date or
959                Observations date. Valid formats are:
960                Absolute as YYYY-MM-DD or YYYYMMDD.
961                Default is an empty string.
962
963            resol: string, optional
964                Specifies the desired triangular truncation of retrieved data,
965                before carrying out any other selected post-processing.
966                The default is automatic truncation (auto), by which the lowest
967                resolution compatible with the value specified in grid is
968                automatically selected for the retrieval.
969                Users wanting to perform post-processing from full spectral
970                resolution should specify Archived Value (av).
971                The following are examples of existing resolutions found in
972                the archive: 63, 106, 159, 213, 255, 319, 399, 511, 799 or 1279.
973                This keyword has no meaning/effect if the archived data is
974                not in spherical harmonics representation.
975                The best selection can be found here:
976                https://software.ecmwf.int/wiki/display/UDOC/\
977                      Retrieve#Retrieve-Truncationbeforeinterpolation
978                Default is an empty string.
979
980            stream: string, optional
981                Identifies the forecasting system used to generate the data.
982                E.g. oper (Atmospheric model), enfo (Ensemble forecats), ...
983                Default is an empty string.
984
985            area: string, optional
986                Specifies the desired sub-area of data to be extracted.
987                Areas can be defined to wrap around the globe.
988
989                Latitude values must be given as signed numbers, with:
990                    north latitudes (i.e. north of the equator)
991                        being positive (e.g: 40.5)
992                    south latitutes (i.e. south of the equator)
993                        being negative (e.g: -50.5)
994                Longtitude values must be given as signed numbers, with:
995                    east longitudes (i.e. east of the 0 degree meridian)
996                        being positive (e.g: 35.0)
997                    west longitudes (i.e. west of the 0 degree meridian)
998                        being negative (e.g: -20.5)
999
1000                E.g.: North/West/South/East
1001                Default is an empty string.
1002
1003            time: string, optional
1004                Specifies the time of the data in hours and minutes.
1005                Valid values depend on the type of data: Analysis time,
1006                Forecast base time or First guess verification time
1007                (all usually at synoptic hours: 00, 06, 12 and 18 ).
1008                Observation time (any combination in hours and minutes is valid,
1009                subject to data availability in the archive).
1010                The syntax is HHMM or HH:MM. If MM is omitted it defaults to 00.
1011                Default is an empty string.
1012
1013            step: string, optional
1014                Specifies the forecast time step from forecast base time.
1015                Valid values are hours (HH) from forecast base time. It also
1016                specifies the length of the forecast which verifies at
1017                First Guess time.
1018                E.g. 1/3/6-hourly
1019                Default is an empty string.
1020
1021            expver: string, optional
1022                The version of the dataset. Each experiment is assigned a
1023                unique code (version). Production data is assigned 1 or 2,
1024                and experimental data in Operations 11, 12 ,...
1025                Research or Member State's experiments have a four letter
1026                experiment identifier.
1027                Default is "1".
1028
1029            number: string, optional
1030                Selects the member in ensemble forecast run. (Only then it
1031                is necessary.) It has a different meaning depending on
1032                the type of data.
1033                E.g. Perturbed Forecasts: specifies the Ensemble forecast member
1034                Default is an empty string.
1035
1036            accuracy: string, optional
1037                Specifies the number of bits per value to be used in the
1038                generated GRIB coded fields.
1039                A positive integer may be given to specify the preferred number
1040                of bits per packed value. This must not be greater than the
1041                number of bits normally used for a Fortran integer on the
1042                processor handling the request (typically 32 or 64 bit).
1043                Within a compute request the accuracy of the original fields
1044                can be passed to the result field by specifying accuracy=av.
1045                Default is an empty string.
1046
1047            grid: string, optional
1048                Specifies the output grid which can be either a Gaussian grid
1049                or a Latitude/Longitude grid. MARS requests specifying
1050                grid=av will return the archived model grid.
1051
1052                Lat/Lon grid: The grid spacing needs to be an integer
1053                fraction of 90 degrees e.g. grid = 0.5/0.5
1054
1055                Gaussian grid: specified by a letter denoting the type of
1056                Gaussian grid followed by an integer (the grid number)
1057                representing the number of lines between the Pole and Equator,
1058                e.g.
1059                grid = F160 - full (or regular) Gaussian grid with
1060                       160 latitude lines between the pole and equator
1061                grid = N320 - ECMWF original reduced Gaussian grid with
1062                       320 latitude lines between the pole and equator,
1063                       see Reduced Gaussian Grids for grid numbers used at ECMWF
1064                grid = O640 - ECMWF octahedral (reduced) Gaussian grid with
1065                       640 latitude lines between the pole and equator
1066                Default is an empty string.
1067
1068            gaussian: string, optional
1069                This parameter is deprecated and should no longer be used.
1070                Specifies the desired type of Gaussian grid for the output.
1071                Valid Gaussian grids are quasi-regular (reduced) or regular.
1072                Keyword gaussian can only be specified together with
1073                keyword grid. Gaussian without grid has no effect.
1074                Default is an empty string.
1075
1076            target: string, optional
1077                Specifies a file into which data is to be written after
1078                retrieval or manipulation. Path names should always be
1079                enclosed in double quotes. The MARS client supports automatic
1080                generation of multiple target files using MARS keywords
1081                enclosed in square brackets [ ].  If the environment variable
1082                MARS_MULTITARGET_STRICT_FORMAT is set to 1 before calling mars,
1083                the keyword values will be used in the filename as shown by
1084                the ecCodes GRIB tool grib_ls -m, e.g. with
1085                MARS_MULTITARGET_STRICT_FORMAT set to 1 the keywords time,
1086                expver and param will be formatted as 0600, 0001 and 129.128
1087                rather than 600, 1 and 129.
1088                Default is an empty string.
1089
1090            param: string, optional
1091                Specifies the meteorological parameter.
1092                The list of meteorological parameters in MARS is extensive.
1093                Their availability is directly related to their meteorological
1094                meaning and, therefore, the rest of directives specified
1095                in the MARS request.
1096                Meteorological parameters can be specified by their
1097                GRIB code (param=130), their mnemonic (param=t) or
1098                full name (param=temperature).
1099                The list of parameter should be seperated by a "/"-sign.
1100                E.g. 130/131/133
1101                Default is an empty string.
1102
1103        @Return:
1104            <nothing>
1105        '''
1106
1107        self.server = server
1108        self.marsclass = marsclass
1109        self.type = type
1110        self.levtype = levtype
1111        self.levelist = levelist
1112        self.repres = repres
1113        self.date = date
1114        self.resol = resol
1115        self.stream = stream
1116        self.area = area
1117        self.time = time
1118        self.step = step
1119        self.expver = expver
1120        self.number = number
1121        self.accuracy = accuracy
1122        self.grid = grid
1123        self.gaussian = gaussian
1124        self.target = target
1125        self.param = param
1126
1127        return
1128
1129
1130    def displayInfo(self):
1131        '''
1132        @Description:
1133            Prints all class attributes and their values.
1134
1135        @Input:
1136            self: instance of MARSretrieval
1137                For description see class documentation.
1138
1139        @Return:
1140            <nothing>
1141        '''
1142        # Get all class attributes and their values as a dictionary
1143        attrs = vars(self)
1144
1145        # iterate through all attributes and print them
1146        # with their corresponding values
1147        for item in attrs.items():
1148            if item[0] in ('server'):
1149                pass
1150            else:
1151                print(item[0] + ': ' + str(item[1]))
1152
1153        return
1154
1155    def dataRetrieve(self):
1156        '''
1157        @Description:
1158            Submits a MARS retrieval. Depending on the existence of
1159            ECMWF Web-API it is submitted via Python or a
1160            subprocess in the Shell. The parameter for the mars retrieval
1161            are taken from the defined class attributes.
1162
1163        @Input:
1164            self: instance of MARSretrieval
1165                For description see class documentation.
1166
1167        @Return:
1168            <nothing>
1169        '''
1170        # Get all class attributes and their values as a dictionary
1171        attrs = vars(self)
1172
1173        # convert the dictionary of attributes into a comma
1174        # seperated list of attributes with their values
1175        # needed for the retrieval call
1176        s = 'ret'
1177        for k, v in attrs.iteritems():
1178            if k in ('server'):
1179                continue
1180            if k == 'marsclass':
1181                k = 'class'
1182            if v == '':
1183                continue
1184            if k.lower() == 'target':
1185                target = v
1186            else:
1187                s = s + ',' + k + '=' + str(v)
1188
1189        # MARS request via Python script
1190        if self.server is not False:
1191            try:
1192                self.server.execute(s, target)
1193            except:
1194                print('MARS Request failed, \
1195                    have you already registered at apps.ecmwf.int?')
1196                raise IOError
1197            if os.stat(target).st_size == 0:
1198                print('MARS Request returned no data - please check request')
1199                raise IOError
1200        # MARS request via extra process in shell
1201        else:
1202            s += ',target = "' + target + '"'
1203            p = subprocess.Popen(['mars'], stdin=subprocess.PIPE,
1204                                 stdout=subprocess.PIPE,
1205                                 stderr=subprocess.PIPE, bufsize=1)
1206            pout = p.communicate(input=s)[0]
1207            print(pout.decode())
1208            if 'Some errors reported' in pout.decode():
1209                print('MARS Request failed - please check request')
1210                raise IOError
1211
1212            if os.stat(target).st_size == 0:
1213                print('MARS Request returned no data - please check request')
1214                raise IOError
1215
1216        return
1217
1218
1219
1220
1221class ECFlexpart:
1222    '''
1223    Class to retrieve ECMWF data specific for running FLEXPART.
1224    '''
1225    def __init__(self, c, fluxes=False): #done/ verstehen
1226        '''
1227        @Description:
1228            Creates an object/instance of ECFlexpart with the
1229            associated settings of its attributes for the retrieval.
1230
1231        @Input:
1232            self: instance of ECFlexpart
1233                The current object of the class.
1234
1235            c: instance of class Control
1236                Contains all the parameters of control files, which are e.g.:
1237                DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
1238                STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
1239                LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
1240                OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
1241                ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
1242                MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR, BASETIME
1243                DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
1244
1245                For more information about format and content of the parameter
1246                see documentation.
1247
1248            fluxes: boolean, optional
1249                Decides if a the flux parameter settings are stored or
1250                the rest of the parameter list.
1251                Default value is False.
1252
1253        @Return:
1254            <nothing>
1255        '''
1256
1257        # different mars types for retrieving reanalysis data for flexpart
1258        self.types = dict()
1259        try:
1260            if c.maxstep > len(c.type):    # Pure forecast mode
1261                c.type = [c.type[1]]
1262                c.step = ['{:0>3}'.format(int(c.step[0]))]
1263                c.time = [c.time[0]]
1264                for i in range(1, c.maxstep + 1):
1265                    c.type.append(c.type[0])
1266                    c.step.append('{:0>3}'.format(i))
1267                    c.time.append(c.time[0])
1268        except:
1269            pass
1270
1271        self.inputdir = c.inputdir
1272        self.basetime = c.basetime
1273        self.dtime = c.dtime
1274        self.mars = {}
1275        i = 0
1276        j = 0
1277        if fluxes is True and c.maxstep < 24:
1278            # only FC with start times at 00/12 (without 00/12)
1279            self.types[c.type[1]] = {'times': '00/12',
1280                                     'steps': '{}/to/12/by/{}'.format(c.dtime,
1281                                                                      c.dtime)}
1282            i = 1
1283            for k in [0, 12]:
1284                for j in range(int(c.dtime), 13, int(c.dtime)):
1285                    self.mars['{:0>3}'.format(i * int(c.dtime))] = \
1286                           [c.type[1], '{:0>2}'.format(k), '{:0>3}'.format(j)]
1287                    i += 1
1288        else:
1289            for ty, st, ti in zip(c.type, c.step, c.time):
1290                btlist = range(24)
1291                if c.basetime == '12':
1292                    btlist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
1293                if c.basetime == '00':
1294                    btlist = [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0]
1295
1296                if mod(i, int(c.dtime)) == 0 and \
1297                    (i in btlist or c.maxstep > 24):
1298
1299                    if ty not in self.types.keys():
1300                        self.types[ty] = {'times': '', 'steps': ''}
1301
1302                    if ti not in self.types[ty]['times']:
1303                        if len(self.types[ty]['times']) > 0:
1304                            self.types[ty]['times'] += '/'
1305                        self.types[ty]['times'] += ti
1306
1307                    if st not in self.types[ty]['steps']:
1308                        if len(self.types[ty]['steps']) > 0:
1309                            self.types[ty]['steps'] += '/'
1310                        self.types[ty]['steps'] += st
1311
1312                    self.mars['{:0>3}'.format(j)] = [ty,
1313                                                     '{:0>2}'.format(int(ti)),
1314                                                     '{:0>3}'.format(int(st))]
1315                    j += int(c.dtime)
1316
1317                i += 1
1318
1319        # Different grids need different retrievals
1320        # SH = Spherical Harmonics, GG = Gaussian Grid,
1321        # OG = Output Grid, ML = MultiLevel, SL = SingleLevel
1322        self.params = {'SH__ML': '', 'SH__SL': '',
1323                       'GG__ML': '', 'GG__SL': '',
1324                       'OG__ML': '', 'OG__SL': '',
1325                       'OG_OROLSM_SL': '', 'OG_acc_SL': ''}
1326        self.marsclass = c.marsclass
1327        self.stream = c.stream
1328        self.number = c.number
1329        self.resol = c.resol
1330        if 'N' in c.grid:  # Gaussian output grid
1331            self.grid = c.grid
1332            self.area = 'G'
1333        else:
1334            self.grid = '{}/{}'.format(int(c.grid) / 1000., int(c.grid) / 1000.)
1335            self.area = '{}/{}/{}/{}'.format(int(c.upper) / 1000.,
1336                                             int(c.left) / 1000.,
1337                                             int(c.lower) / 1000.,
1338                                             int(c.right) / 1000.)
1339
1340        self.accuracy = c.accuracy
1341        self.level = c.level
1342        try:
1343            self.levelist = c.levelist
1344        except:
1345            self.levelist = '1/to/' + c.level
1346
1347        self.glevelist = '1/to/' + c.level
1348
1349        try:
1350            self.gaussian = c.gaussian
1351        except:
1352            self.gaussian = ''
1353
1354        try:
1355            self.dataset = c.dataset
1356        except:
1357            self.dataset = ''
1358
1359        try:
1360            self.expver = c.expver
1361        except:
1362            self.expver = '1'
1363
1364        try:
1365            self.number = c.number
1366        except:
1367            self.number = '0'
1368
1369        self.outputfilelist = []
1370
1371
1372        # Now comes the nasty part that deals with the different
1373        # scenarios we have:
1374        # 1) Calculation of etadot on
1375        #    a) Gaussian grid
1376        #    b) Output grid
1377        #    c) Output grid using parameter 77 retrieved from MARS
1378        # 3) Calculation/Retrieval of omega
1379        # 4) Download also data for WRF
1380
1381        if fluxes is False:
1382            self.params['SH__SL'] = ['LNSP', 'ML', '1', 'OFF']
1383    #        self.params['OG__SL'] = ["SD/MSL/TCC/10U/10V/2T/2D/129/172",
1384    #                                 'SFC','1',self.grid]
1385            self.params['OG__SL'] = ["141/151/164/165/166/167/168/129/172", \
1386                                     'SFC', '1', self.grid]
1387            self.params['OG_OROLSM__SL'] = ["160/27/28/173", \
1388                                            'SFC', '1', self.grid]
1389
1390            if len(c.addpar) > 0:
1391                if c.addpar[0] == '/':
1392                    c.addpar = c.addpar[1:]
1393                self.params['OG__SL'][0] += '/' + '/'.join(c.addpar)
1394            self.params['OG__ML'] = ['T/Q', 'ML', self.levelist, self.grid]
1395
1396            if c.gauss == '0' and c.eta == '1':
1397                # the simplest case
1398                self.params['OG__ML'][0] += '/U/V/77'
1399            elif c.gauss == '0' and c.eta == '0':
1400                # this is not recommended (inaccurate)
1401                self.params['OG__ML'][0] += '/U/V'
1402            elif c.gauss == '1' and c.eta == '0':
1403                # this is needed for data before 2008, or for reanalysis data
1404                self.params['GG__SL'] = ['Q', 'ML', '1', \
1405                                         '{}'.format((int(self.resol) + 1) / 2)]
1406                self.params['SH__ML'] = ['U/V/D', 'ML', self.glevelist, 'OFF']
1407            else:
1408                print('Warning: This is a very costly parameter combination, \
1409                       use only for debugging!')
1410                self.params['GG__SL'] = ['Q', 'ML', '1', \
1411                                         '{}'.format((int(self.resol) + 1) / 2)]
1412                self.params['GG__ML'] = ['U/V/D/77', 'ML', self.glevelist, \
1413                                         '{}'.format((int(self.resol) + 1) / 2)]
1414
1415            if c.omega == '1':
1416                self.params['OG__ML'][0] += '/W'
1417
1418            try:
1419                # add cloud water content if necessary
1420                if c.cwc == '1':
1421                    self.params['OG__ML'][0] += '/CLWC/CIWC'
1422            except:
1423                pass
1424
1425            try:
1426                # add vorticity and geopotential height for WRF if necessary
1427                if c.wrf == '1':
1428                    self.params['OG__ML'][0] += '/Z/VO'
1429                    if '/D' not in self.params['OG__ML'][0]:
1430                        self.params['OG__ML'][0] += '/D'
1431                    #wrf_sfc = 'sp/msl/skt/2t/10u/10v/2d/z/lsm/sst/ci/sd/stl1/ /
1432                    #           stl2/stl3/stl4/swvl1/swvl2/swvl3/swvl4'.upper()
1433                    wrf_sfc = '134/235/167/165/166/168/129/172/34/31/141/ \
1434                               139/170/183/236/39/40/41/42'.upper()
1435                    lwrt_sfc = wrf_sfc.split('/')
1436                    for par in lwrt_sfc:
1437                        if par not in self.params['OG__SL'][0]:
1438                            self.params['OG__SL'][0] += '/' + par
1439            except:
1440                pass
1441        else:
1442            self.params['OG_acc_SL'] = ["LSP/CP/SSHF/EWSS/NSSS/SSR", \
1443                                        'SFC', '1', self.grid]
1444
1445        # if needed, add additional WRF specific parameters here
1446
1447        return
1448
1449
1450    def write_namelist(self, c, filename): #done
1451        '''
1452        @Description:
1453            Creates a namelist file in the temporary directory and writes
1454            the following values to it: maxl, maxb, mlevel,
1455            mlevelist, mnauf, metapar, rlo0, rlo1, rla0, rla1,
1456            momega, momegadiff, mgauss, msmooth, meta, metadiff, mdpdeta
1457
1458        @Input:
1459            self: instance of ECFlexpart
1460                The current object of the class.
1461
1462            c: instance of class Control
1463                Contains all the parameters of control files, which are e.g.:
1464                DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
1465                STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
1466                LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
1467                OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
1468                ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
1469                MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR, BASETIME
1470                DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
1471
1472                For more information about format and content of the parameter
1473                see documentation.
1474
1475            filename: string
1476                Name of the namelist file.
1477
1478        @Return:
1479            <nothing>
1480        '''
1481
1482        self.inputdir = c.inputdir
1483        area = asarray(self.area.split('/')).astype(float)
1484        grid = asarray(self.grid.split('/')).astype(float)
1485
1486        if area[1] > area[3]:
1487            area[1] -= 360
1488        zyk = abs((area[3] - area[1] - 360.) + grid[1]) < 1.e-6
1489        maxl = int((area[3] - area[1]) / grid[1]) + 1
1490        maxb = int((area[0] - area[2]) / grid[0]) + 1
1491
1492        with open(self.inputdir + '/' + filename, 'w') as f:
1493            f.write('&NAMGEN\n')
1494            f.write(',\n  '.join(['maxl = ' + str(maxl), 'maxb = ' + str(maxb),
1495                    'mlevel = ' + self.level,
1496                    'mlevelist = ' + '"' + self.levelist + '"',
1497                    'mnauf = ' + self.resol, 'metapar = ' + '77',
1498                    'rlo0 = ' + str(area[1]), 'rlo1 = ' + str(area[3]),
1499                    'rla0 = ' + str(area[2]), 'rla1 = ' + str(area[0]),
1500                    'momega = ' + c.omega, 'momegadiff = ' + c.omegadiff,
1501                    'mgauss = ' + c.gauss, 'msmooth = ' + c.smooth,
1502                    'meta = ' + c.eta, 'metadiff = ' + c.etadiff,
1503                    'mdpdeta = ' + c.dpdeta]))
1504
1505            f.write('\n/\n')
1506
1507        return
1508
1509    def retrieve(self, server, dates, times, inputdir=''):
1510        '''
1511        @Description:
1512
1513
1514        @Input:
1515            self: instance of ECFlexpart
1516
1517            server: instance of ECMWFService
1518
1519            dates:
1520
1521            times:
1522
1523            inputdir: string, optional
1524                Default string is empty ('').
1525
1526        @Return:
1527            <nothing>
1528        '''
1529        self.dates = dates
1530        self.server = server
1531
1532        if inputdir == "":
1533            self.inputdir = '.'
1534        else:
1535            self.inputdir = inputdir
1536
1537        # Retrieve Q not for using Q but as a template for a reduced gaussian
1538        # grid one date and time is enough
1539        # Take analysis at 00
1540        qdate = self.dates
1541        idx = qdate.find("/")
1542        if (idx > 0):
1543            qdate = self.dates[:idx]
1544
1545        #QG =  MARSretrieval(self.server, dataset = self.dataset, marsclass = self.marsclass, stream = self.stream, type = "an", levtype = "ML", levelist = "1",
1546                             #gaussian = "reduced",grid = '{}'.format((int(self.resol)+1)/2), resol = self.resol,accuracy = self.accuracy,target = self.inputdir+"/"+"QG.grb",
1547                             #date = qdate, time = "00",expver = self.expver, param = "133.128")
1548        #QG.displayInfo()
1549        #QG.dataRetrieve()
1550
1551        oro = False
1552        for ftype in self.types:
1553            for pk, pv in self.params.iteritems():
1554                if isinstance(pv, str):     # or pk != 'GG__SL' :
1555                    continue
1556                mftype = ''+ftype
1557                mftime = self.types[ftype]['times']
1558                mfstep = self.types[ftype]['steps']
1559                mfdate = self.dates
1560                mfstream = self.stream
1561                mftarget = self.inputdir + "/" + ftype + pk + '.' + \
1562                           self.dates.split('/')[0] + '.' + str(os.getppid()) +\
1563                           '.' + str(os.getpid()) + ".grb"
1564                if pk == 'OG__SL':
1565                    pass
1566                if pk == 'OG_OROLSM__SL':
1567                    if oro is False:
1568                        mfstream = 'OPER'
1569                        mftype = 'AN'
1570                        mftime = '00'
1571                        mfstep = '000'
1572                        mfdate = self.dates.split('/')[0]
1573                        mftarget = self.inputdir + "/" + pk + '.' + mfdate + \
1574                                   '.' + str(os.getppid()) + '.' + \
1575                                   str(os.getpid()) + ".grb"
1576                        oro = True
1577                    else:
1578                        continue
1579
1580                if pk == 'GG__SL' and pv[0] == 'Q':
1581                    area = ""
1582                    gaussian = 'reduced'
1583                else:
1584                    area = self.area
1585                    gaussian = self.gaussian
1586
1587                if self.basetime is None:
1588                    MR =  MARSretrieval(self.server,
1589                            marsclass=self.marsclass, stream=mfstream,
1590                            type=mftype, levtype=pv[1], levelist=pv[2],
1591                            resol=self.resol, gaussian=gaussian,
1592                            accuracy=self.accuracy, grid=pv[3],
1593                            target=mftarget, area=area, date=mfdate,
1594                            time=mftime, number=self.number, step=mfstep,
1595                            expver=self.expver, param=pv[0])
1596
1597                    MR.displayInfo()
1598                    MR.dataRetrieve()
1599    # The whole else section is only necessary for operational scripts.
1600    # It could be removed
1601                else:
1602                    # check if mars job requests fields beyond basetime.
1603                    # If yes eliminate those fields since they may not
1604                    # be accessible with user's credentials
1605                    sm1 = -1
1606                    if 'by' in mfstep:
1607                        sm1 = 2
1608                    tm1 = -1
1609                    if 'by' in mftime:
1610                        tm1 = 2
1611                    maxtime = datetime.datetime.strptime(
1612                                mfdate.split('/')[-1] + mftime.split('/')[tm1],
1613                                '%Y%m%d%H') + datetime.timedelta(
1614                                hours=int(mfstep.split('/')[sm1]))
1615
1616                    elimit = datetime.datetime.strptime(
1617                                mfdate.split('/')[-1] +
1618                                self.basetime, '%Y%m%d%H')
1619
1620                    if self.basetime == '12':
1621                        if 'acc' in pk:
1622
1623                # Strategy: if maxtime-elimit> = 24h reduce date by 1,
1624                # if 12h< = maxtime-elimit<12h reduce time for last date
1625                # if maxtime-elimit<12h reduce step for last time
1626                # A split of the MARS job into 2 is likely necessary.
1627                            maxtime = elimit-datetime.timedelta(hours=24)
1628                            mfdate = '/'.join(('/'.join(mfdate.split('/')[:-1]),
1629                                                datetime.datetime.strftime(
1630                                                maxtime, '%Y%m%d')))
1631
1632                            MR = MARSretrieval(self.server,
1633                                            marsclass=self.marsclass,
1634                                            stream=self.stream, type=mftype,
1635                                            levtype=pv[1], levelist=pv[2],
1636                                            resol=self.resol, gaussian=gaussian,
1637                                            accuracy=self.accuracy, grid=pv[3],
1638                                            target=mftarget, area=area,
1639                                            date=mfdate, time=mftime,
1640                                            number=self.number, step=mfstep,
1641                                            expver=self.expver, param=pv[0])
1642
1643                            MR.displayInfo()
1644                            MR.dataRetrieve()
1645
1646                            maxtime = elimit - datetime.timedelta(hours=12)
1647                            mfdate = datetime.datetime.strftime(maxtime,
1648                                                                '%Y%m%d')
1649                            mftime = '00'
1650                            mftarget = self.inputdir + "/" + ftype + pk + \
1651                                       '.' + mfdate + '.' + str(os.getppid()) +\
1652                                       '.' + str(os.getpid()) + ".grb"
1653
1654                            MR = MARSretrieval(self.server,
1655                                            marsclass=self.marsclass,
1656                                            stream=self.stream, type=mftype,
1657                                            levtype=pv[1], levelist=pv[2],
1658                                            resol=self.resol, gaussian=gaussian,
1659                                            accuracy=self.accuracy, grid=pv[3],
1660                                            target=mftarget, area=area,
1661                                            date=mfdate, time=mftime,
1662                                            number=self.number, step=mfstep,
1663                                            expver=self.expver, param=pv[0])
1664
1665                            MR.displayInfo()
1666                            MR.dataRetrieve()
1667                        else:
1668                            MR = MARSretrieval(self.server,
1669                                            marsclass=self.marsclass,
1670                                            stream=self.stream, type=mftype,
1671                                            levtype=pv[1], levelist=pv[2],
1672                                            resol=self.resol, gaussian=gaussian,
1673                                            accuracy=self.accuracy, grid=pv[3],
1674                                            target=mftarget, area=area,
1675                                            date=mfdate, time=mftime,
1676                                            number=self.number, step=mfstep,
1677                                            expver=self.expver, param=pv[0])
1678
1679                            MR.displayInfo()
1680                            MR.dataRetrieve()
1681                    else:
1682                        maxtime = elimit - datetime.timedelta(hours=24)
1683                        mfdate = datetime.datetime.strftime(maxtime,'%Y%m%d')
1684
1685                        mftimesave = ''.join(mftime)
1686
1687                        if '/' in mftime:
1688                            times = mftime.split('/')
1689                            while ((int(times[0]) +
1690                                   int(mfstep.split('/')[0]) <= 12) and
1691                                  (pk != 'OG_OROLSM__SL') and 'acc' not in pk):
1692                                times = times[1:]
1693                            if len(times) > 1:
1694                                mftime = '/'.join(times)
1695                            else:
1696                                mftime = times[0]
1697
1698                        MR = MARSretrieval(self.server,
1699                                        marsclass=self.marsclass,
1700                                        stream=self.stream, type=mftype,
1701                                        levtype=pv[1], levelist=pv[2],
1702                                        resol=self.resol, gaussian=gaussian,
1703                                        accuracy=self.accuracy, grid=pv[3],
1704                                        target=mftarget, area=area,
1705                                        date=mfdate, time=mftime,
1706                                        number=self.number, step=mfstep,
1707                                        expver=self.expver, param=pv[0])
1708
1709                        MR.displayInfo()
1710                        MR.dataRetrieve()
1711
1712                        if (int(mftimesave.split('/')[0]) == 0 and
1713                            int(mfstep.split('/')[0]) == 0 and
1714                            pk != 'OG_OROLSM__SL'):
1715                            mfdate = datetime.datetime.strftime(elimit,'%Y%m%d')
1716                            mftime = '00'
1717                            mfstep = '000'
1718                            mftarget = self.inputdir + "/" + ftype + pk + \
1719                                       '.' + mfdate + '.' + str(os.getppid()) +\
1720                                       '.' + str(os.getpid()) + ".grb"
1721
1722                            MR = MARSretrieval(self.server,
1723                                        marsclass=self.marsclass,
1724                                        stream=self.stream, type=mftype,
1725                                        levtype=pv[1], levelist=pv[2],
1726                                        resol=self.resol, gaussian=gaussian,
1727                                        accuracy=self.accuracy, grid=pv[3],
1728                                        target=mftarget, area=area,
1729                                        date=mfdate, time=mftime,
1730                                        number=self.number, step=mfstep,
1731                                        expver=self.expver, param=pv[0])
1732
1733                            MR.displayInfo()
1734                            MR.dataRetrieve()
1735
1736            print("MARS retrieve done... ")
1737
1738            return
1739
1740
1741    def process_output(self, c): #done
1742        '''
1743        @Description:
1744            The grib files are postprocessed depending on selection in
1745            control file. The following modifications might be done if
1746            properly switched in control file:
1747            GRIB2 - Conversion to GRIB2
1748            ECTRANS - Transfer of files to gateway server
1749            ECSTORAGE - Storage at ECMWF server
1750            GRIB2FLEXPART - Conversion of GRIB files to FLEXPART binary format
1751
1752        @Input:
1753            self: instance of ECFlexpart
1754                The current object of the class.
1755
1756            c: instance of class Control
1757                Contains all the parameters of control files, which are e.g.:
1758                DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
1759                STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
1760                LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
1761                OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
1762                ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
1763                MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR, BASETIME
1764                DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
1765
1766                For more information about format and content of the parameter
1767                see documentation.
1768
1769        @Return:
1770            <nothing>
1771
1772        '''
1773
1774        print('Postprocessing:\n Format: {}\n'.format(c.format))
1775
1776        if c.ecapi is False:
1777            print('ecstorage: {}\n ecfsdir: {}\n'.
1778                  format(c.ecstorage, c.ecfsdir))
1779            if not hasattr(c, 'gateway'):
1780                c.gateway = os.getenv('GATEWAY')
1781            if not hasattr(c, 'destination'):
1782                c.destination = os.getenv('DESTINATION')
1783            print('ectrans: {}\n gateway: {}\n destination: {}\n '
1784                    .format(c.ectrans, c.gateway, c.destination))
1785
1786        print('Output filelist: \n', self.outputfilelist)
1787
1788        if c.format.lower() == 'grib2':
1789            for ofile in self.outputfilelist:
1790                p = subprocess.check_call(['grib_set', '-s', 'edition=2, \
1791                                            productDefinitionTemplateNumber=8',
1792                                            ofile, ofile + '_2'])
1793                p = subprocess.check_call(['mv', ofile + '_2', ofile])
1794
1795        if int(c.ectrans) == 1 and c.ecapi is False:
1796            for ofile in self.outputfilelist:
1797                p = subprocess.check_call(['ectrans', '-overwrite', '-gateway',
1798                                           c.gateway, '-remote', c.destination,
1799                                           '-source', ofile])
1800                print('ectrans:', p)
1801
1802        if int(c.ecstorage) == 1 and c.ecapi is False:
1803            for ofile in self.outputfilelist:
1804                p = subprocess.check_call(['ecp', '-o', ofile,
1805                                           os.path.expandvars(c.ecfsdir)])
1806
1807        if c.outputdir != c.inputdir:
1808            for ofile in self.outputfilelist:
1809                p = subprocess.check_call(['mv', ofile, c.outputdir])
1810
1811        # prepare environment for the grib2flexpart run
1812        # to convert grib to flexpart binary
1813        if c.grib2flexpart == '1':
1814
1815            # generate AVAILABLE file
1816            # Example of AVAILABLE file data
1817            # 20131107 000000      EN13110700              ON DISC
1818            clist = []
1819            for ofile in self.outputfilelist:
1820                fname = ofile.split('/')
1821                if '.' in fname[-1]:
1822                    l = fname[-1].split('.')
1823                    timestamp = datetime.datetime.strptime(l[0][-6:] + l[1],
1824                                                           '%y%m%d%H')
1825                    timestamp += datetime.timedelta(hours=int(l[2]))
1826                    cdate = datetime.datetime.strftime(timestamp, '%Y%m%d')
1827                    chms = datetime.datetime.strftime(timestamp, '%H%M%S')
1828                else:
1829                    cdate = '20' + fname[-1][-8:-2]
1830                    chms = fname[-1][-2:] + '0000'
1831                clist.append(cdate + ' ' + chms + ' '*6 +
1832                             fname[-1] + ' '*14 + 'ON DISC')
1833            clist.sort()
1834            with open(c.outputdir + '/' + 'AVAILABLE', 'w') as f:
1835                f.write('\n'.join(clist) + '\n')
1836
1837            # generate pathnames file
1838            pwd = os.path.abspath(c.outputdir)
1839            with open(pwd + '/pathnames','w') as f:
1840                f.write(pwd + '/Options/\n')
1841                f.write(pwd + '/\n')
1842                f.write(pwd + '/\n')
1843                f.write(pwd + '/AVAILABLE\n')
1844                f.write(' = == = == = == = == = == ==  = \n')
1845
1846            # create Options dir if necessary
1847            if not os.path.exists(pwd + '/Options'):
1848                os.makedirs(pwd+'/Options')
1849
1850            # read template COMMAND file
1851            with open(os.path.expandvars(
1852                     os.path.expanduser(c.flexpart_root_scripts)) +
1853                     '/../Options/COMMAND', 'r') as f:
1854                lflist = f.read().split('\n')
1855
1856            # find index of list where to put in the
1857            # date and time information
1858            # usually after the LDIRECT parameter
1859            i = 0
1860            for l in lflist:
1861                if 'LDIRECT' in l.upper():
1862                    break
1863                i += 1
1864
1865#            clist.sort()
1866            # insert the date and time information of run star and end
1867            # into the list of lines of COMMAND file
1868            lflist = lflist[:i+1] + \
1869                     [clist[0][:16], clist[-1][:16]] + \
1870                     lflist[i+3:]
1871
1872            # write the new COMMAND file
1873            with open(pwd + '/Options/COMMAND', 'w') as g:
1874                g.write('\n'.join(lflist) + '\n')
1875
1876            # change to outputdir and start the
1877            # grib2flexpart run
1878            # afterwards switch back to the working dir
1879            os.chdir(c.outputdir)
1880            p = subprocess.check_call([os.path.expandvars(
1881                        os.path.expanduser(c.flexpart_root_scripts)) +
1882                        '/../FLEXPART_PROGRAM/grib2flexpart',
1883                        'useAvailable', '.'])
1884            os.chdir(pwd)
1885
1886        return
1887
1888    def create(self, inputfiles, c): #done
1889        '''
1890        @Description:
1891            This method is based on the ECMWF example index.py
1892            https://software.ecmwf.int/wiki/display/GRIB/index.py
1893
1894            An index file will be created which depends on the combination
1895            of "date", "time" and "stepRange" values. This is used to iterate
1896            over all messages in the grib files passed through the parameter
1897            "inputfiles" to seperate specific parameters into fort.* files.
1898            Afterwards the FORTRAN program Convert2 is called to convert
1899            the data fields all to the same grid and put them in one file
1900            per day.
1901
1902        @Input:
1903            self: instance of ECFlexpart
1904                The current object of the class.
1905
1906            inputfiles: instance of UIOFiles
1907                Contains a list of files.
1908
1909            c: instance of class Control
1910                Contains all the parameters of control files, which are e.g.:
1911                DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
1912                STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
1913                LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
1914                OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
1915                ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
1916                MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR, BASETIME
1917                DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
1918
1919                For more information about format and content of the parameter
1920                see documentation.
1921
1922        @Return:
1923            <nothing>
1924        '''
1925
1926        table128 = init128(c.ecmwfdatadir +
1927                           '/grib_templates/ecmwf_grib1_table_128')
1928        wrfpars = toparamId('sp/mslp/skt/2t/10u/10v/2d/z/lsm/sst/ci/sd/\
1929                            stl1/stl2/stl3/stl4/swvl1/swvl2/swvl3/swvl4',
1930                            table128)
1931
1932        index_keys = ["date", "time", "step"]
1933        indexfile = c.inputdir + "/date_time_stepRange.idx"
1934        silentremove(indexfile)
1935        grib = GribTools(inputfiles.files)
1936        # creates new index file
1937        iid = grib.index(index_keys=index_keys, index_file=indexfile)
1938
1939        # read values of index keys
1940        index_vals = []
1941        for key in index_keys:
1942            index_vals.append(grib_index_get(iid, key))
1943            print(index_vals[-1])
1944            # index_vals looks for example like:
1945            # index_vals[0]: ('20171106', '20171107', '20171108') ; date
1946            # index_vals[1]: ('0', '1200', '1800', '600') ; time
1947            # index_vals[2]: ('0', '12', '3', '6', '9') ; stepRange
1948
1949
1950        for prod in product(*index_vals):
1951            # e.g. prod = ('20170505', '0', '12')
1952            #             (  date    ,time, step)
1953            # per date e.g. time = 0, 600, 1200, 1800
1954            # per time e.g. step = 0, 3, 6, 9, 12
1955            for i in range(len(index_keys)):
1956                grib_index_select(iid, index_keys[i], prod[i])
1957
1958            gid = grib_new_from_index(iid)
1959            # do convert2 program if gid at this time is not None,
1960            # therefore save in hid
1961            hid = gid
1962            if gid is not None:
1963                cdate = str(grib_get(gid, 'date'))
1964                time = grib_get(gid, 'time')
1965                type = grib_get(gid, 'type')
1966                step = grib_get(gid, 'step')
1967                # create correct timestamp from the three time informations
1968                # date, time, step
1969                timestamp = datetime.datetime.strptime(
1970                                cdate + '{:0>2}'.format(time/100), '%Y%m%d%H')
1971                timestamp += datetime.timedelta(hours=int(step))
1972
1973                cdateH = datetime.datetime.strftime(timestamp, '%Y%m%d%H')
1974                chms = datetime.datetime.strftime(timestamp, '%H%M%S')
1975
1976                if c.basetime is not None:
1977                    slimit = datetime.datetime.strptime(
1978                                c.start_date + '00', '%Y%m%d%H')
1979                    bt = '23'
1980                    if c.basetime == '00':
1981                        bt = '00'
1982                        slimit = datetime.datetime.strptime(
1983                                    c.end_date + bt, '%Y%m%d%H') - \
1984                                    datetime.timedelta(hours=12-int(c.dtime))
1985                    if c.basetime == '12':
1986                        bt = '12'
1987                        slimit = datetime.datetime.strptime(
1988                                    c.end_date + bt, '%Y%m%d%H') - \
1989                                 datetime.timedelta(hours=12-int(c.dtime))
1990
1991                    elimit = datetime.datetime.strptime(
1992                                c.end_date + bt, '%Y%m%d%H')
1993
1994                    if timestamp < slimit or timestamp > elimit:
1995                        continue
1996
1997            try:
1998                if c.wrf == '1':
1999                    if 'olddate' not in locals():
2000                        fwrf = open(c.outputdir + '/WRF' + cdate +
2001                                    '.{:0>2}'.format(time) + '.000.grb2', 'w')
2002                        olddate = cdate[:]
2003                    else:
2004                        if cdate != olddate:
2005                            fwrf = open(c.outputdir + '/WRF' + cdate +
2006                                        '.{:0>2}'.format(time) + '.000.grb2',
2007                                        'w')
2008                            olddate = cdate[:]
2009            except AttributeError:
2010                pass
2011
2012            # delete old fort.* files and open them newly
2013            fdict = {'10':None, '11':None, '12':None, '13':None, '16':None,
2014                         '17':None, '19':None, '21':None, '22':None, '20':None}
2015            #for f in fdict.keys():
2016            #    silentremove(c.inputdir + "/fort." + f)
2017            for k, f in fdict.iteritems():
2018                silentremove(c.inputdir + "/fort." + k)
2019                fdict[k] = open(c.inputdir + '/fort.' + k, 'w')
2020
2021            savedfields = []
2022            while 1:
2023                if gid is None:
2024                    break
2025                paramId = grib_get(gid, 'paramId')
2026                gridtype = grib_get(gid, 'gridType')
2027                datatype = grib_get(gid, 'dataType')
2028                levtype = grib_get(gid, 'typeOfLevel')
2029                if paramId == 133 and gridtype == 'reduced_gg':
2030                # Relative humidity (Q.grb) is used as a template only
2031                # so we need the first we "meet"
2032                    with open(c.inputdir + '/fort.18', 'w') as fout:
2033                        grib_write(gid, fout)
2034                elif paramId == 131 or paramId == 132:
2035                    grib_write(gid, fdict['10'])
2036                elif paramId == 130:
2037                    grib_write(gid, fdict['11'])
2038                elif paramId == 133 and gridtype != 'reduced_gg':
2039                    grib_write(gid, fdict['17'])
2040                elif paramId == 152:
2041                    grib_write(gid, fdict['12'])
2042                elif paramId == 155 and gridtype == 'sh':
2043                    grib_write(gid, fdict['13'])
2044                elif paramId in [129, 138, 155] and levtype == 'hybrid' \
2045                                                and c.wrf == '1':
2046                    pass
2047                elif paramId == 246 or paramId == 247:
2048                    # cloud liquid water and ice
2049                    if paramId == 246:
2050                        clwc = grib_get_values(gid)
2051                    else:
2052                        clwc += grib_get_values(gid)
2053                        grib_set_values(gid, clwc)
2054                        grib_set(gid, 'paramId', 201031)
2055                        grib_write(gid, fdict['22'])
2056                elif paramId == 135:
2057                    grib_write(gid, fdict['19'])
2058                elif paramId == 77:
2059                    grib_write(gid, fdict['21'])
2060                else:
2061                    if paramId not in savedfields:
2062                        grib_write(gid, fdict['16'])
2063                        savedfields.append(paramId)
2064                    else:
2065                        print('duplicate ' + str(paramId) + ' not written')
2066
2067                try:
2068                    if c.wrf == '1':
2069# die if abfrage scheint ueberfluessig da eh das gleihce ausgefuehrt wird
2070                        if levtype == 'hybrid':
2071                            if paramId in [129, 130, 131, 132, 133, 138, 155]:
2072                                grib_write(gid, fwrf)
2073                        else:
2074                            if paramId in wrfpars:
2075                                grib_write(gid, fwrf)
2076                except AttributeError:
2077                    pass
2078
2079                grib_release(gid)
2080                gid = grib_new_from_index(iid)
2081
2082        for f in fdict.values():
2083            f.close()
2084
2085        # call for CONVERT2
2086# AUSLAGERN IN EIGENE FUNKTION
2087
2088        if hid is not None:
2089            pwd = os.getcwd()
2090            os.chdir(c.inputdir)
2091            if os.stat('fort.21').st_size == 0 and int(c.eta) == 1:
2092                print('Parameter 77 (etadot) is missing, most likely it is \
2093                       not available for this type or date/time\n')
2094                print('Check parameters CLASS, TYPE, STREAM, START_DATE\n')
2095                myerror(c, 'fort.21 is empty while parameter eta is set \
2096                            to 1 in CONTROL file')
2097
2098            p = subprocess.check_call([os.path.expandvars(
2099                    os.path.expanduser(c.exedir)) + '/CONVERT2'], shell=True)
2100            os.chdir(pwd)
2101            # create the corresponding output file fort.15
2102            # (generated by CONVERT2)
2103            # + fort.16 (paramId 167 and paramId 168)
2104            fnout = c.inputdir + '/' + c.prefix
2105            if c.maxstep > 12:
2106                suffix = cdate[2:8] + '.{:0>2}'.format(time/100) + \
2107                         '.{:0>3}'.format(step)
2108            else:
2109                suffix = cdateH[2:10]
2110
2111            fnout += suffix
2112            print("outputfile = " + fnout)
2113            self.outputfilelist.append(fnout) # needed for final processing
2114            fout = open(fnout, 'wb')
2115            shutil.copyfileobj(open(c.inputdir + '/fort.15', 'rb'), fout)
2116            if c.cwc == '1':
2117                shutil.copyfileobj(open(c.inputdir + '/fort.22', 'rb'), fout)
2118            shutil.copyfileobj(open(c.inputdir + '/flux' + cdate[0:2] +
2119                                    suffix, 'rb'), fout)
2120            shutil.copyfileobj(open(c.inputdir + '/fort.16', 'rb'), fout)
2121            orolsm = glob.glob(c.inputdir +
2122                               '/OG_OROLSM__SL.*.' + c.ppid + '*')[0]
2123            shutil.copyfileobj(open(orolsm, 'rb'), fout)
2124            fout.close()
2125            if c.omega == '1':
2126                fnout = c.outputdir + '/OMEGA'
2127                fout  =  open(fnout, 'wb')
2128                shutil.copyfileobj(open(c.inputdir + '/fort.25', 'rb'), fout)
2129
2130        try:
2131            if c.wrf == '1':
2132                fwrf.close()
2133        except:
2134            pass
2135
2136        grib_index_release(iid)
2137
2138        return
2139
2140
2141    def deacc_fluxes(self, inputfiles, c):
2142        '''
2143        @Description:
2144
2145
2146        @Input:
2147            self: instance of ECFlexpart
2148                The current object of the class.
2149
2150            inputfiles: instance of UIOFiles
2151                Contains a list of files.
2152
2153            c: instance of class Control
2154                Contains all the parameters of control files, which are e.g.:
2155                DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
2156                STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
2157                LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
2158                OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
2159                ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
2160                MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR, BASETIME
2161                DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
2162
2163                For more information about format and content of the parameter
2164                see documentation.
2165
2166        @Return:
2167            <nothing>
2168        '''
2169
2170        table128 = init128(c.ecmwfdatadir +
2171                           '/grib_templates/ecmwf_grib1_table_128')
2172        pars = toparamId(self.params['OG_acc_SL'][0], table128)
2173
2174        index_keys = ["date", "time", "step"]
2175        indexfile = c.inputdir + "/date_time_stepRange.idx"
2176        silentremove(indexfile)
2177        grib = GribTools(inputfiles.files)
2178        # creates new index file
2179        iid = grib.index(index_keys=index_keys, index_file=indexfile)
2180
2181        # read values of index keys
2182        index_vals = []
2183        for key in index_keys:
2184            key_vals = grib_index_get(iid,key)
2185            print(key_vals)
2186            # have to sort the steps for disaggregation,
2187            # therefore convert to int first
2188            if key == 'step':
2189                key_vals = [int(k) for k in key_vals]
2190                key_vals.sort()
2191                key_vals = [str(k) for k in key_vals]
2192            index_vals.append(key_vals)
2193            # index_vals looks for example like:
2194            # index_vals[0]: ('20171106', '20171107', '20171108') ; date
2195            # index_vals[1]: ('0', '1200', '1800', '600') ; time
2196            # index_vals[2]: ('0', '12', '3', '6', '9') ; stepRange
2197
2198        valsdict = {}
2199        svalsdict = {}
2200        stepsdict = {}
2201        for p in pars:
2202            valsdict[str(p)] = []
2203            svalsdict[str(p)] = []
2204            stepsdict[str(p)] = []
2205 # ab hier eien Einrückung zurück!!!!
2206            for prod in product(*index_vals):
2207                # e.g. prod = ('20170505', '0', '12')
2208                #             (  date    ,time, step)
2209                # per date e.g. time = 0, 600, 1200, 1800
2210                # per time e.g. step = 0, 3, 6, 9, 12
2211                for i in range(len(index_keys)):
2212                    grib_index_select(iid, index_keys[i], prod[i])
2213
2214                gid = grib_new_from_index(iid)
2215                # do convert2 program if gid at this time is not None,
2216                # therefore save in hid
2217                hid = gid
2218                if gid is not None:
2219                    cdate = str(grib_get(gid, 'date'))
2220                    time = grib_get(gid, 'time')
2221                    type = grib_get(gid, 'type')
2222                    step = grib_get(gid, 'step')
2223                    # date+time+step-2*dtime
2224                    #(since interpolated value valid for step-2*dtime)
2225                    sdate = datetime.datetime(year=cdate / 10000,
2226                                              month=mod(cdate, 10000) / 100,
2227                                              day=mod(cdate, 100),
2228                                              hour=time / 100)
2229                    fdate = sdate + datetime.timedelta(
2230                                        hours=step - 2 * int(c.dtime))
2231                    sdates = sdate + datetime.timedelta(hours=step)
2232                else:
2233                    break
2234
2235            if c.maxstep > 12:
2236                fnout = c.inputdir + '/flux' + \
2237                    sdate.strftime('%Y%m%d') + '.{:0>2}'.format(time/100) + \
2238                    '.{:0>3}'.format(step-2*int(c.dtime))
2239                gnout = c.inputdir + '/flux' + \
2240                    sdate.strftime('%Y%m%d')+'.{:0>2}'.format(time/100) + \
2241                    '.{:0>3}'.format(step-int(c.dtime))
2242                hnout = c.inputdir + '/flux' + \
2243                    sdate.strftime('%Y%m%d')+'.{:0>2}'.format(time/100) + \
2244                    '.{:0>3}'.format(step)
2245                g = open(gnout, 'w')
2246                h = open(hnout, 'w')
2247            else:
2248                fnout = c.inputdir + '/flux' + fdate.strftime('%Y%m%d%H')
2249                gnout = c.inputdir + '/flux' + (fdate+datetime.timedelta(
2250                    hours = int(c.dtime))).strftime('%Y%m%d%H')
2251                hnout = c.inputdir + '/flux' + sdates.strftime('%Y%m%d%H')
2252                g = open(gnout, 'w')
2253                h = open(hnout, 'w')
2254            print("outputfile = " + fnout)
2255            f = open(fnout, 'w')
2256
2257            while 1:
2258                if gid is None:
2259                    break
2260                cparamId = str(grib_get(gid, 'paramId'))
2261                step = grib_get(gid, 'step')
2262                atime = grib_get(gid, 'time')
2263                ni = grib_get(gid, 'Ni')
2264                nj = grib_get(gid, 'Nj')
2265                if cparamId in valsdict.keys():
2266                    values = grib_get_values(gid)
2267                    vdp = valsdict[cparamId]
2268                    svdp = svalsdict[cparamId]
2269                    sd = stepsdict[cparamId]
2270
2271                    if cparamId == '142' or cparamId == '143':
2272                        fak = 1./1000.
2273                    else:
2274                        fak = 3600.
2275
2276                    values = (reshape(values, (nj, ni))).flatten() / fak
2277                    vdp.append(values[:])  # save the accumulated values
2278                    if step <= int(c.dtime):
2279                        svdp.append(values[:] / int(c.dtime))
2280                    else: # deaccumulate values
2281                        svdp.append((vdp[-1] - vdp[-2]) / int(c.dtime))
2282
2283                    print(cparamId, atime, step, len(values),
2284                          values[0], std(values))
2285                    # save the 1/3-hourly or specific values
2286                    # svdp.append(values[:])
2287                    sd.append(step)
2288                    # len(svdp) correspond to the time
2289                    if len(svdp) >= 3:
2290                        if len(svdp) > 3:
2291                            if cparamId == '142' or cparamId == '143':
2292                                values = darain(svdp)
2293                            else:
2294                                values = dapoly(svdp)
2295
2296                            if not (step == c.maxstep and c.maxstep > 12 \
2297                                    or sdates == elimit):
2298                                vdp.pop(0)
2299                                svdp.pop(0)
2300                        else:
2301                            if c.maxstep > 12:
2302                                values = svdp[1]
2303                            else:
2304                                values = svdp[0]
2305
2306                        grib_set_values(gid, values)
2307                        if c.maxstep > 12:
2308                            grib_set(gid, 'step', max(0, step-2*int(c.dtime)))
2309                        else:
2310                            grib_set(gid, 'step', 0)
2311                            grib_set(gid, 'time', fdate.hour*100)
2312                            grib_set(gid, 'date', fdate.year*10000 +
2313                                     fdate.month*100+fdate.day)
2314                        grib_write(gid, f)
2315
2316                        if c.basetime is not None:
2317                            elimit = datetime.datetime.strptime(c.end_date +
2318                                                                c.basetime,
2319                                                                '%Y%m%d%H')
2320                        else:
2321                            elimit = sdate + datetime.timedelta(2*int(c.dtime))
2322
2323                        # squeeze out information of last two steps contained
2324                        # in svdp
2325                        # if step+int(c.dtime) == c.maxstep and c.maxstep>12
2326                        # or sdates+datetime.timedelta(hours = int(c.dtime))
2327                        # >= elimit:
2328                        # Note that svdp[0] has not been popped in this case
2329
2330                        if (step == c.maxstep and c.maxstep > 12
2331                            or sdates == elimit):
2332                            values = svdp[3]
2333                            grib_set_values(gid, values)
2334                            grib_set(gid, 'step', 0)
2335                            truedatetime = fdate + datetime.timedelta(
2336                                     hours=2*int(c.dtime))
2337                            grib_set(gid, 'time', truedatetime.hour * 100)
2338                            grib_set(gid, 'date', truedatetime.year * 10000 +
2339                                     truedatetime.month * 100 +
2340                                     truedatetime.day)
2341                            grib_write(gid, h)
2342
2343                            #values = (svdp[1]+svdp[2])/2.
2344                            if cparamId == '142' or cparamId == '143':
2345                                values = darain(list(reversed(svdp)))
2346                            else:
2347                                values = dapoly(list(reversed(svdp)))
2348
2349                            grib_set(gid, 'step',0)
2350                            truedatetime = fdate + datetime.timedelta(
2351                                     hours=int(c.dtime))
2352                            grib_set(gid, 'time', truedatetime.hour * 100)
2353                            grib_set(gid, 'date', truedatetime.year * 10000 +
2354                                     truedatetime.month * 100 +
2355                                     truedatetime.day)
2356                            grib_set_values(gid, values)
2357                            grib_write(gid, g)
2358
2359                    grib_release(gid)
2360
2361            gid = grib_new_from_index(iid)
2362
2363            f.close()
2364            g.close()
2365            h.close()
2366
2367            grib_index_release(iid)
2368
2369        return
2370
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG