source: flex_extract.git/python/pythontest/TestInstallTar/test_untar/python/ControlFile.py @ 2fb99de

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

introduced config with path definitions and changed py files accordingly; Installation works; some tests were added for tarball making; Problems in submission to ecgate

  • Property mode set to 100644
File size: 18.0 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
59import _config
60
61# ------------------------------------------------------------------------------
62# CLASS
63# ------------------------------------------------------------------------------
64class ControlFile(object):
65    '''
66    Class containing the information of the flex_extract CONTROL file.
67
68    Contains all the parameters of CONTROL file, which are e.g.:
69    DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME,
70    STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT,
71    LOWER, UPPER, RIGHT, LEVEL, LEVELIST, RESOL, GAUSS, ACCURACY,
72    OMEGA, OMEGADIFF, ETA, ETADIFF, DPDETA, SMOOTH, FORMAT,
73    ADDPAR, WRF, CWC, PREFIX, ECSTORAGE, ECTRANS, ECFSDIR,
74    MAILOPS, MAILFAIL, GRIB2FLEXPART, FLEXPARTDIR,
75    BASETIME, DATE_CHUNK, DEBUG, INPUTDIR, OUTPUTDIR, FLEXPART_ROOT_SCRIPTS
76
77    For more information about format and content of the parameter
78    see documentation.
79
80    '''
81
82    def __init__(self, filename):
83        '''
84        @Description:
85            Initialises the instance of ControlFile class and defines
86            all class attributes with default values. Afterwards calls
87            function __read_controlfile__ to read parameter from
88            Control file.
89
90        @Input:
91            self: instance of ControlFile class
92                Description see class documentation.
93
94            filename: string
95                Name of CONTROL file.
96
97        @Return:
98            <nothing>
99        '''
100
101        # list of all possible class attributes and their default values
102        self.controlfile = filename
103        self.start_date = None
104        self.end_date = None
105        self.date_chunk = 3
106        self.dtime = None
107        self.basetime = None
108        self.maxstep = None
109        self.type = None
110        self.time = None
111        self.step = None
112        self.marsclass = None
113        self.stream = None
114        self.number = 'OFF'
115        self.expver = None
116        self.grid = None
117        self.area = ''
118        self.left = None
119        self.lower = None
120        self.upper = None
121        self.right = None
122        self.level = None
123        self.levelist = None
124        self.resol = None
125        self.gauss = 0
126        self.accuracy = 24
127        self.omega = 0
128        self.omegadiff = 0
129        self.eta = 0
130        self.etadiff = 0
131        self.etapar = 77
132        self.dpdeta = 1
133        self.smooth = 0
134        self.format = 'GRIB1'
135        self.addpar = None
136        self.prefix = 'EN'
137        self.cwc = 0
138        self.wrf = 0
139        self.ecfsdir = 'ectmp:/${USER}/econdemand/'
140        self.mailfail = ['${USER}']
141        self.mailops = ['${USER}']
142        self.grib2flexpart = 0
143        self.ecstorage = 0
144        self.ectrans = 0
145        self.inputdir = '../work'
146        self.outputdir = self.inputdir
147        self.ecmwfdatadir = None
148        self.exedir = None
149        self.flexpart_root_scripts = None
150        self.makefile = None
151        self.destination = None
152        self.gateway = None
153        self.ecuid = None
154        self.ecgid = None
155        self.install_target = None
156        self.debug = 0
157        self.request = 0
158
159        self.__read_controlfile__()
160
161        return
162
163    def __read_controlfile__(self):
164        '''
165        @Description:
166            Read CONTROL file and assign all CONTROL file variables.
167
168        @Input:
169            self: instance of ControlFile class
170                Description see class documentation.
171
172        @Return:
173            <nothing>
174        '''
175        from tools import my_error
176
177        # read whole CONTROL file
178        with open(self.controlfile) as f:
179            fdata = f.read().split('\n')
180
181        # go through every line and store parameter
182        for ldata in fdata:
183            data = ldata.split()
184            if len(data) > 1:
185                if 'm_' in data[0].lower():
186                    data[0] = data[0][2:]
187                if data[0].lower() == 'class':
188                    data[0] = 'marsclass'
189                if data[0].lower() == 'day1':
190                    data[0] = 'start_date'
191                if data[0].lower() == 'day2':
192                    data[0] = 'end_date'
193                if data[0].lower() == 'addpar':
194                    if '/' in data[1]:
195                        # remove leading '/' sign from addpar content
196                        if data[1][0] == '/':
197                            data[1] = data[1][1:]
198                        dd = data[1].split('/')
199                        data = [data[0]]
200                        for d in dd:
201                            data.append(d)
202                if len(data) == 2:
203                    if '$' in data[1]:
204                        setattr(self, data[0].lower(), data[1])
205                        while '$' in data[1]:
206                            i = data[1].index('$')
207                            j = data[1].find('{')
208                            k = data[1].find('}')
209                            var = os.getenv(data[1][j+1:k])
210                            if var is not None:
211                                data[1] = data[1][:i] + var + data[1][k+1:]
212                            else:
213                                my_error(self.mailfail,
214                                         'Could not find variable '
215                                         + data[1][j+1:k] + ' while reading ' +
216                                         self.controlfile)
217                        setattr(self, data[0].lower() + '_expanded', data[1])
218                    else:
219                        if data[1].lower() != 'none':
220                            setattr(self, data[0].lower(), data[1])
221                        else:
222                            setattr(self, data[0].lower(), None)
223                elif len(data) > 2:
224                    setattr(self, data[0].lower(), (data[1:]))
225            else:
226                pass
227
228        # script directory
229        self.ecmwfdatadir = os.path.dirname(os.path.abspath(inspect.getfile(
230            inspect.currentframe()))) + '/../'
231
232        # Fortran source directory
233        self.exedir = self.ecmwfdatadir + 'src/'
234
235        return
236
237    def __str__(self):
238        '''
239        @Description:
240            Prepares a string which have all the ControlFile
241            class attributes with its associated values.
242            Each attribute is printed in one line and in
243            alphabetical order.
244
245            Example:
246            'age': 10
247            'color': 'Spotted'
248            'kids': 0
249            'legs': 2
250            'name': 'Dog'
251            'smell': 'Alot'
252
253        @Input:
254            self: instance of ControlFile class
255                Description see class documentation.
256
257        @Return:
258            string of ControlFile class attributes with their values
259        '''
260        import collections
261
262        attrs = vars(self)
263        attrs = collections.OrderedDict(sorted(attrs.items()))
264
265        return '\n'.join("%s: %s" % item for item in attrs.items())
266
267    def assign_args_to_control(self, args):
268        '''
269        @Description:
270            Overwrites the existing ControlFile instance attributes with
271            the command line arguments.
272
273        @Input:
274            self: instance of ControlFile class
275                Description see class documentation.
276
277            args: instance of ArgumentParser
278                Contains the commandline arguments from script/program call.
279
280        @Return:
281            <nothing>
282        '''
283
284        # get dictionary of command line parameters and eliminate all
285        # parameters which are None (were not specified)
286        args_dict = vars(args)
287        arguments = {k : args_dict[k] for k in args_dict
288                     if args_dict[k] != None}
289
290        # assign all passed command line arguments to ControlFile instance
291        for k, v in arguments.iteritems():
292            setattr(self, str(k), v)
293
294        return
295
296    def assign_envs_to_control(self, envs):
297        '''
298        @Description:
299            Assigns the ECMWF environment parameter.
300
301        @Input:
302            envs: dict of strings
303                Contains the ECMWF environment parameternames "ECUID", "ECGID",
304                "DESTINATION" and "GATEWAY" with its corresponding values.
305                They were read from the file "ECMWF_ENV".
306
307        @Return:
308            <nothing>
309        '''
310
311        for k, v in envs.iteritems():
312            setattr(self, str(k).lower(), str(v))
313
314        return
315
316    def check_conditions(self):
317        '''
318        @Description:
319            Checks a couple of necessary attributes and conditions,
320            such as if they exist and contain values.
321            Otherwise set default values.
322
323        @Input:
324            self: instance of ControlFile class
325                Description see class documentation.
326
327        @Return:
328            <nothing>
329        '''
330        from tools import my_error
331        import numpy as np
332
333        # check for having at least a starting date
334        # otherwise program is not allowed to run
335        if self.start_date is None:
336            print 'start_date specified neither in command line nor ' + \
337                  'in CONTROL file ' +  self.controlfile
338            print 'Try "' + sys.argv[0].split('/')[-1] + \
339                  ' -h" to print usage information'
340            sys.exit(1)
341
342        # retrieve just one day if end_date isn't set
343        if self.end_date is None:
344            self.end_date = self.start_date
345
346        # assure consistency of levelist and level
347        if self.levelist is None:
348            if self.level is None:
349                print 'Warning: neither levelist nor level ' + \
350                      'specified in CONTROL file'
351                sys.exit(1)
352            else:
353                self.levelist = '1/to/' + self.level
354        else:
355            if 'to' in self.levelist.lower():
356                self.level = self.levelist.split('/')[2]
357            else:
358                self.level = self.levelist.split('/')[-1]
359
360        # if area was provided at command line
361        # decompse area into its 4 components
362        if self.area:
363            afloat = '.' in self.area
364            l = self.area.split('/')
365            if afloat:
366                for i, item in enumerate(l):
367                    item = str(int(float(item) * 1000))
368            self.upper, self.left, self.lower, self.right = l
369
370        # prepare step for correct usage
371        if '/' in self.step:
372            l = self.step.split('/')
373            if 'to' in self.step.lower():
374                if 'by' in self.step.lower():
375                    ilist = np.arange(int(l[0]), int(l[2]) + 1, int(l[4]))
376                    self.step = ['{:0>3}'.format(i) for i in ilist]
377                else:
378                    my_error(self.mailfail, self.step + ':\n' +
379                             'if "to" is used, please use "by" as well')
380            else:
381                self.step = l
382
383        # if maxstep wasn't provided
384        # search for it in the "step" parameter
385        if self.maxstep is None:
386            self.maxstep = 0
387            for s in self.step:
388                if int(s) > self.maxstep:
389                    self.maxstep = int(s)
390        else:
391            self.maxstep = int(self.maxstep)
392
393        # set root scripts since it is needed later on
394        if not self.flexpart_root_scripts:
395            self.flexpart_root_scripts = self.ecmwfdatadir
396
397        if not isinstance(self.mailfail, list):
398            if ',' in self.mailfail:
399                self.mailfail = self.mailfail.split(',')
400            elif ' ' in self.mailfail:
401                self.mailfail = self.mailfail.split()
402            else:
403                self.mailfail = [self.mailfail]
404
405        if not isinstance(self.mailops, list):
406            if ',' in self.mailops:
407                self.mailops = self.mailops.split(',')
408            elif ' ' in self.mailops:
409                self.mailops = self.mailops.split()
410            else:
411                self.mailops = [self.mailops]
412
413        if not self.gateway or not self.destination or \
414           not self.ecuid or not self.ecgid:
415            print '\nEnvironment variables GATWAY, DESTINATION, ECUID and ' + \
416                  'ECGID were not set properly!'
417            print 'Please check for excistence of file "ECMWF_ENV" in the ' + \
418                  'python directory!'
419            sys.exit(1)
420
421        if self.request != 0:
422            marsfile = os.path.join(_config.PATH_RUN_DIR + os.path.sep +
423                                    _config.FILE_MARS_REQUESTS)
424            if os.path.isfile(marsfile):
425                os.remove(marsfile)
426
427        # check logical variables for data type
428        # if its a string change to integer
429        logicals = ['gauss', 'omega', 'omegadiff', 'eta', 'etadiff',
430                    'dpdeta', 'cwc', 'wrf', 'grib2flexpart', 'ecstorage',
431                    'ectrans', 'debug', 'request']
432
433        for var in logicals:
434            if not isinstance(getattr(self, var), int):
435                setattr(self, var, int(getattr(self, var)))
436
437        return
438
439    def check_install_conditions(self):
440        '''
441        @Description:
442            Checks a couple of necessary attributes and conditions
443            for the installation such as if they exist and contain values.
444            Otherwise set default values.
445
446        @Input:
447            self: instance of ControlFile class
448                Description see class documentation.
449
450        @Return:
451            <nothing>
452        '''
453
454        if self.install_target and \
455           self.install_target not in ['local', 'ecgate', 'cca']:
456            print('ERROR: unknown or missing installation target ')
457            print('target: ', self.install_target)
458            print('please specify correct installation target ' +
459                  '(local | ecgate | cca)')
460            print('use -h or --help for help')
461            sys.exit(1)
462
463        if self.install_target and self.install_target != 'local':
464            if not self.ecgid or not self.ecuid or \
465               not self.gateway or not self.destination:
466                print('Please enter your ECMWF user id and group id as well ' +
467                      'as the \nname of the local gateway and the ectrans ' +
468                      'destination ')
469                print('with command line options --ecuid --ecgid \
470                       --gateway --destination')
471                print('Try "' + sys.argv[0].split('/')[-1] + \
472                      ' -h" to print usage information')
473                print('Please consult ecaccess documentation or ECMWF user \
474                       support for further details')
475                sys.exit(1)
476
477            if not self.flexpart_root_scripts:
478                self.flexpart_root_scripts = '${HOME}'
479            else:
480                self.flexpart_root_scripts = self.flexpart_root_scripts
481        else: # local
482            if not self.flexpart_root_scripts:
483                self.flexpart_root_scripts = '../'
484
485        if not self.makefile:
486            self.makefile = 'Makefile.gfortran'
487
488        return
489
490    def to_list(self):
491        '''
492        @Description:
493            Just generates a list of strings containing the attributes and
494            assigned values except the attributes "_expanded", "exedir",
495            "ecmwfdatadir" and "flexpart_root_scripts".
496
497        @Input:
498            self: instance of ControlFile class
499                Description see class documentation.
500
501        @Return:
502            l: list
503                A sorted list of the all ControlFile class attributes with
504                their values except the attributes "_expanded", "exedir",
505                "ecmwfdatadir" and "flexpart_root_scripts".
506        '''
507
508        import collections
509
510        attrs = collections.OrderedDict(sorted(vars(self).items()))
511
512        l = list()
513
514        for item in attrs.items():
515            if '_expanded' in item[0]:
516                pass
517            elif 'exedir' in item[0]:
518                pass
519            elif 'flexpart_root_scripts' in item[0]:
520                pass
521            elif 'ecmwfdatadir' in item[0]:
522                pass
523            else:
524                if isinstance(item[1], list):
525                    stot = ''
526                    for s in item[1]:
527                        stot += s + ' '
528
529                    l.append("%s %s" % (item[0], stot))
530                else:
531                    l.append("%s %s" % item)
532
533        return sorted(l)
534
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG