source: flex_extract.git/source/python/classes/ControlFile.py @ c274d9a

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

added rrint param and updated deacc_fluxes function to store rain fields

  • Property mode set to 100644
File size: 17.8 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#*******************************************************************************
4# @Author: Leopold Haimberger (University of Vienna)
5#
6# @Date: November 2015
7#
8# @Change History:
9#
10#   February 2018 - Anne Philipp (University of Vienna):
11#        - applied PEP8 style guide
12#        - added documentation
13#        - applied some minor modifications in programming style/structure
14#        - changed name of class Control to ControlFile for more
15#          self-explanation naming
16#        - outsource of class ControlFile
17#        - initialisation of class attributes ( to avoid high number of
18#          conditional statements and set default values )
19#        - divided assignment of attributes and the check of conditions
20#        - outsourced the commandline argument assignments to control attributes
21#
22# @License:
23#    (C) Copyright 2015-2018.
24#
25#    This software is licensed under the terms of the Apache Licence Version 2.0
26#    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
27#
28# @Class Description:
29#    The CONTROL file is the steering part of the FLEXPART extraction
30#    software. All necessary parameters needed to retrieve the data fields
31#    from the MARS archive for driving FLEXPART are set in a CONTROL file.
32#    Some specific parameters like the start and end dates can be overwritten
33#    by the command line parameters, but in generel all parameters needed
34#    for a complete set of fields for FLEXPART can be set in the CONTROL file.
35#
36# @Class Content:
37#    - __init__
38#    - __read_controlfile__
39#    - __str__
40#    - assign_args_to_control
41#    - assign_envs_to_control
42#    - check_conditions
43#    - check_install_conditions
44#    - to_list
45#
46# @Class Attributes:
47#
48#
49#*******************************************************************************
50
51# ------------------------------------------------------------------------------
52# MODULES
53# ------------------------------------------------------------------------------
54import os
55import re
56import sys
57import inspect
58
59# software specific classes and modules from flex_extract
60sys.path.append('../')
61import _config
62from mods.tools import my_error, silent_remove
63from mods.checks import check_grid, check_area, check_levels
64
65# ------------------------------------------------------------------------------
66# CLASS
67# ------------------------------------------------------------------------------
68class ControlFile(object):
69    '''
70    Contains the information which are stored in the CONTROL files.
71    '''
72
73    def __init__(self, filename):
74        '''Initialises the instance of ControlFile class and defines
75        all class attributes with default values. Afterwards calls
76        function __read_controlfile__ to read parameter from Control file.
77
78        Parameters
79        ----------
80        filename : :obj:`string`
81            Name of CONTROL file.
82
83        Return
84        ------
85
86        '''
87
88        # list of all possible class attributes and their default values
89        self.controlfile = filename
90        self.start_date = None
91        self.end_date = None
92        self.date_chunk = 3
93        self.dtime = None
94        self.basetime = None
95        self.maxstep = None
96        self.type = None
97        self.time = None
98        self.step = None
99        self.acctype = None
100        self.acctime = None
101        self.accmaxstep = None
102        self.marsclass = None
103        self.dataset = None
104        self.stream = None
105        self.number = 'OFF'
106        self.expver = '1'
107        self.gaussian = ''
108        self.grid = None
109        self.area = ''
110        self.left = None
111        self.lower = None
112        self.upper = None
113        self.right = None
114        self.level = None
115        self.levelist = None
116        self.resol = None
117        self.gauss = 0
118        self.accuracy = 24
119        self.omega = 0
120        self.omegadiff = 0
121        self.eta = 0
122        self.etadiff = 0
123        self.etapar = 77
124        self.dpdeta = 1
125        self.smooth = 0
126        self.format = 'GRIB1'
127        self.addpar = None
128        self.prefix = 'EN'
129        self.cwc = 0
130        self.wrf = 0
131        self.ecfsdir = 'ectmp:/${USER}/econdemand/'
132        self.mailfail = ['${USER}']
133        self.mailops = ['${USER}']
134        self.grib2flexpart = 0
135        self.ecstorage = 0
136        self.ectrans = 0
137        self.inputdir = _config.PATH_INPUT_DIR
138        self.outputdir = None
139        self.ecmwfdatadir = _config.PATH_FLEXEXTRACT_DIR
140        self.exedir = _config.PATH_FORTRAN_SRC
141        self.flexpart_root_scripts = None
142        self.makefile = 'Makefile.gfortran'
143        self.destination = None
144        self.gateway = None
145        self.ecuid = None
146        self.ecgid = None
147        self.install_target = None
148        self.debug = 0
149        self.request = 0
150        self.public = 0
151        self.ecapi = None
152        self.rrint = 0
153
154        self.logicals = ['gauss', 'omega', 'omegadiff', 'eta', 'etadiff',
155                         'dpdeta', 'cwc', 'wrf', 'grib2flexpart', 'ecstorage',
156                         'ectrans', 'debug', 'request', 'public', 'rrint']
157
158        self.__read_controlfile__()
159
160        return
161
162    def __read_controlfile__(self):
163        '''Read CONTROL file and assign all CONTROL file variables.
164
165        Parameters
166        ----------
167
168        Return
169        ------
170
171        '''
172
173        try:
174            cfile = os.path.join(_config.PATH_CONTROLFILES, self.controlfile)
175            with open(cfile) as f:
176                fdata = f.read().split('\n')
177        except IOError:
178            print('Could not read CONTROL file "' + cfile + '"')
179            print('Either it does not exist or its syntax is wrong.')
180            print('Try "' + sys.argv[0].split('/')[-1] + \
181                      ' -h" to print usage information')
182            sys.exit(1)
183
184        # go through every line and store parameter
185        for ldata in fdata:
186            data = ldata.split()
187            if len(data) > 1:
188                if 'm_' in data[0].lower():
189                    data[0] = data[0][2:]
190                if data[0].lower() == 'class':
191                    data[0] = 'marsclass'
192                if data[0].lower() == 'day1':
193                    data[0] = 'start_date'
194                if data[0].lower() == 'day2':
195                    data[0] = 'end_date'
196                if data[0].lower() == 'addpar':
197                    if '/' in data[1]:
198                        # remove leading '/' sign from addpar content
199                        if data[1][0] == '/':
200                            data[1] = data[1][1:]
201                        dd = data[1].split('/')
202                        data = [data[0]]
203                        for d in dd:
204                            data.append(d)
205                if len(data) == 2:
206                    if '$' in data[1]:
207                        setattr(self, data[0].lower(), data[1])
208                        while '$' in data[1]:
209                            i = data[1].index('$')
210                            j = data[1].find('{')
211                            k = data[1].find('}')
212                            var = os.getenv(data[1][j+1:k])
213                            if var is not None:
214                                data[1] = data[1][:i] + var + data[1][k+1:]
215                            else:
216                                my_error(self.mailfail,
217                                         'Could not find variable '
218                                         + data[1][j+1:k] + ' while reading ' +
219                                         self.controlfile)
220                        setattr(self, data[0].lower() + '_expanded', data[1])
221                    else:
222                        if data[1].lower() != 'none':
223                            setattr(self, data[0].lower(), data[1])
224                        else:
225                            setattr(self, data[0].lower(), None)
226                elif len(data) > 2:
227                    setattr(self, data[0].lower(), (data[1:]))
228            else:
229                pass
230
231        return
232
233    def __str__(self):
234        '''Prepares a string which have all the ControlFile class attributes
235        with its associated values. Each attribute is printed in one line and
236        in alphabetical order.
237
238        Example
239        -------
240        'age': 10
241        'color': 'Spotted'
242        'kids': 0
243        'legs': 2
244        'name': 'Dog'
245        'smell': 'Alot'
246
247        Parameters
248        ----------
249
250        Return
251        ------
252        string
253            Single string of concatenated ControlFile class attributes
254            with their values
255        '''
256        import collections
257
258        attrs = vars(self).copy()
259        attrs = collections.OrderedDict(sorted(attrs.items()))
260
261        return '\n'.join("%s: %s" % item for item in attrs.items())
262
263    def assign_args_to_control(self, args):
264        '''Overwrites the existing ControlFile instance attributes with
265        the command line arguments.
266
267        Parameters
268        ----------
269        args : :obj:`Namespace`
270            Contains the commandline arguments from script/program call.
271
272        Return
273        ------
274
275        '''
276
277        # get dictionary of command line parameters and eliminate all
278        # parameters which are None (were not specified)
279        args_dict = vars(args)
280        arguments = {k : args_dict[k] for k in args_dict
281                     if args_dict[k] != None}
282
283        # assign all passed command line arguments to ControlFile instance
284        for k, v in arguments.iteritems():
285            setattr(self, str(k), v)
286
287        return
288
289    def assign_envs_to_control(self, envs):
290        '''Assigns the ECMWF environment parameter.
291
292        Parameters
293        ----------
294        envs : :obj:`dictionary` of :obj:`strings`
295            Contains the ECMWF environment parameternames "ECUID", "ECGID",
296            "DESTINATION" and "GATEWAY" with its corresponding values.
297            They were read from the file "ECMWF_ENV".
298
299        Return
300        ------
301
302        '''
303
304        for k, v in envs.iteritems():
305            setattr(self, str(k).lower(), str(v))
306
307        return
308
309    def check_conditions(self, queue):
310        '''Checks a couple of necessary attributes and conditions,
311        such as if they exist and contain values.
312        Otherwise set default values.
313
314        Parameters
315        ----------
316        queue : :obj:`string`
317            Name of the queue if submitted to the ECMWF servers.
318            Used to check if ecuid, ecgid, gateway and destination
319            are set correctly and are not empty.
320
321        Return
322        ------
323
324        '''
325        from mods.tools import my_error
326        import numpy as np
327
328        # check for having at least a starting date
329        # otherwise program is not allowed to run
330        if not self.start_date:
331            print('start_date specified neither in command line nor '
332                  'in CONTROL file ' +  self.controlfile)
333            print('Try "' + sys.argv[0].split('/')[-1] +
334                  ' -h" to print usage information')
335            sys.exit(1)
336
337        # retrieve just one day if end_date isn't set
338        if not self.end_date:
339            self.end_date = self.start_date
340
341        # basetime has only two possible values
342        if self.basetime:
343            if int(self.basetime) != 0 and int(self.basetime) != 12:
344                print('Basetime has an invalid value!')
345                print('Basetime = ' + str(self.basetime))
346                sys.exit(1)
347
348        self.levelist, self.level = check_levels(self.levelist, self.level)
349
350        # # assure consistency of levelist and level
351        # if not self.levelist and not self.level:
352            # print('Warning: neither levelist nor level \
353                               # specified in CONTROL file')
354            # sys.exit(1)
355        # elif not self.levelist and self.level:
356            # self.levelist = '1/to/' + self.level
357        # elif (self.levelist and not self.level) or \
358             # (self.levelist[-1] != self.level[-1]):
359            # self.level = self.levelist.split('/')[-1]
360        # else:
361            # pass
362
363        # # check if max level is a valid level
364        # if int(self.level) not in _config.MAX_LEVEL_LIST:
365            # print('ERROR: ')
366            # print('LEVEL must be the maximum level of a specified '
367                  # 'level list from ECMWF, e.g.')
368            # print(_config.MAX_LEVEL_LIST)
369            # print('Check parameter "LEVEL" or the max level of "LEVELIST"!')
370            # sys.exit(1)
371
372        # prepare step list if "/" signs are found
373        if '/' in self.step:
374            steps = self.step.split('/')
375            if 'to' in self.step.lower() and 'by' in self.step.lower():
376                ilist = np.arange(int(steps[0]),
377                                  int(steps[2]) + 1,
378                                  int(steps[4]))
379                self.step = ['{:0>3}'.format(i) for i in ilist]
380            elif 'to' in self.step.lower() and 'by' not in self.step.lower():
381                my_error(self.mailfail, self.step + ':\n' +
382                         'if "to" is used in steps parameter, '
383                         'please use "by" as well')
384            else:
385                self.step = steps
386
387        # if maxstep wasn't provided
388        # search for it in the "step" parameter
389        if not self.maxstep:
390            self.maxstep = 0
391            for s in self.step:
392                if int(s) > self.maxstep:
393                    self.maxstep = int(s)
394        else:
395            self.maxstep = int(self.maxstep)
396
397        # set root scripts since it is needed later on
398        if not self.flexpart_root_scripts:
399            self.flexpart_root_scripts = self.ecmwfdatadir
400
401        if not self.outputdir:
402            self.outputdir = self.inputdir
403
404        if not isinstance(self.mailfail, list):
405            if ',' in self.mailfail:
406                self.mailfail = self.mailfail.split(',')
407            elif ' ' in self.mailfail:
408                self.mailfail = self.mailfail.split()
409            else:
410                self.mailfail = [self.mailfail]
411
412        if not isinstance(self.mailops, list):
413            if ',' in self.mailops:
414                self.mailops = self.mailops.split(',')
415            elif ' ' in self.mailops:
416                self.mailops = self.mailops.split()
417            else:
418                self.mailops = [self.mailops]
419
420        if queue in _config.QUEUES_LIST and \
421           not self.gateway or not self.destination or \
422           not self.ecuid or not self.ecgid:
423            print('\nEnvironment variables GATEWAY, DESTINATION, ECUID and '
424                  'ECGID were not set properly!')
425            print('Please check for existence of file "ECMWF_ENV" in the '
426                  'python directory!')
427            sys.exit(1)
428
429        if self.request != 0:
430            marsfile = os.path.join(self.inputdir,
431                                    _config.FILE_MARS_REQUESTS)
432            if os.path.isfile(marsfile):
433                silent_remove(marsfile)
434
435        # check all logical variables for data type
436        # if its a string change to integer
437        for var in self.logicals:
438            if not isinstance(getattr(self, var), int):
439                setattr(self, var, int(getattr(self, var)))
440
441        if self.public and not self.dataset:
442            print('ERROR: ')
443            print('If public mars data wants to be retrieved, '
444                  'the "dataset"-parameter has to be set in the control file!')
445            sys.exit(1)
446
447        if not isinstance(self.type, list):
448            self.type = [self.type]
449
450        for i, val in enumerate(self.type):
451            if self.type[i] == 'AN' and int(self.step[i]) != 0:
452                print('Analysis retrievals must have STEP = 0 (is set to 0)')
453                self.type[i] = 0
454
455        if not isinstance(self.time, list):
456            self.time = [self.time]
457
458        if not isinstance(self.step, list):
459            self.step = [self.step]
460
461        if not self.acctype:
462            print('... Control paramter ACCTYPE was not defined.')
463            try:
464                if len(self.type) > 1 and self.type[1] != 'AN':
465                    print('Use old setting by using TYPE[1] for flux forecast!')
466                    self.acctype = self.type[1]
467            except:
468                print('Use default value "FC" for flux forecast!')
469                self.acctype='FC'
470
471        if not self.acctime:
472            print('... Control paramter ACCTIME was not defined.')
473            print('Use default value "00/12" for flux forecast!')
474            self.acctime='00/12'
475
476        if not self.accmaxstep:
477            print('... Control paramter ACCMAXSTEP was not defined.')
478            print('Use default value "12" for flux forecast!')
479            self.accmaxstep='12'
480
481        self.grid = check_grid(self.grid)
482
483        self.area = check_area(self.grid, self.area, self.upper, self.lower,
484                               self.left, self.right)
485
486
487        return
488
489    def to_list(self):
490        '''Just generates a list of strings containing the attributes and
491        assigned values except the attributes "_expanded", "exedir",
492        "ecmwfdatadir" and "flexpart_root_scripts".
493
494        Parameters
495        ----------
496
497        Return
498        ------
499        l : :obj:`list`
500            A sorted list of the all ControlFile class attributes with
501            their values except the attributes "_expanded", "exedir",
502            "ecmwfdatadir" and "flexpart_root_scripts".
503        '''
504
505        import collections
506
507        attrs = collections.OrderedDict(sorted(vars(self).copy().items()))
508
509        l = list()
510
511        for item in attrs.items():
512            if '_expanded' in item[0]:
513                pass
514            elif 'exedir' in item[0]:
515                pass
516            elif 'flexpart_root_scripts' in item[0]:
517                pass
518            elif 'ecmwfdatadir' in item[0]:
519                pass
520            else:
521                if isinstance(item[1], list):
522                    stot = ''
523                    for s in item[1]:
524                        stot += s + ' '
525
526                    l.append("%s %s\n" % (item[0], stot))
527                else:
528                    l.append("%s %s\n" % item)
529
530        return sorted(l)
531
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG