source: flex_extract.git/source/python/classes/ControlFile.py @ 6f951ca

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

new style of docstring params and updates in docstrings

  • Property mode set to 100644
File size: 22.3 KB
RevLine 
[812283d]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
[54a8a01]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
[812283d]21#
22# @License:
[6f951ca]23#    (C) Copyright 2014-2019.
24#    Anne Philipp, Leopold Haimberger
[54a8a01]25#
[6f951ca]26#    This work is licensed under the Creative Commons Attribution 4.0
27#    International License. To view a copy of this license, visit
28#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
29#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
[812283d]30#
[6f951ca]31# @Class Methods:
32#    __init__
33#    _read_controlfile
34#    __str__
35#    assign_args_to_control
36#    assign_envs_to_control
37#    check_conditions
38#    check_install_conditions
39#    to_list
[812283d]40#*******************************************************************************
41
42# ------------------------------------------------------------------------------
43# MODULES
44# ------------------------------------------------------------------------------
45import os
[2fb99de]46import re
[54a8a01]47import sys
[812283d]48import inspect
[145fbe0]49import numpy as np
[ff99eae]50
[ca867de]51# software specific classes and modules from flex_extract
52sys.path.append('../')
[2fb99de]53import _config
[70fee58]54from mods.tools import my_error, silent_remove
[145fbe0]55from mods.checks import (check_grid, check_area, check_levels, check_purefc,
56                         check_step, check_mail, check_queue, check_pathes,
57                         check_dates, check_maxstep, check_type, check_request,
58                         check_basetime, check_public, check_acctype,
59                         check_acctime, check_accmaxstep, check_time,
60                         check_logicals_type, check_len_type_time_step,
[f2616a3]61                         check_addpar, check_job_chunk)
[2fb99de]62
[812283d]63# ------------------------------------------------------------------------------
64# CLASS
65# ------------------------------------------------------------------------------
[ff99eae]66class ControlFile(object):
[812283d]67    '''
[708c667]68    Contains the information which are stored in the CONTROL files.
[6f951ca]69
70    The CONTROL file is the steering part of the FLEXPART extraction
71    software. All necessary parameters needed to retrieve the data fields
72    from the MARS archive for driving FLEXPART are set in a CONTROL file.
73    Some specific parameters like the start and end dates can be overwritten
74    by the command line parameters, but in generel all parameters needed
75    for a complete set of fields for FLEXPART can be set in the CONTROL file.
76
77    Attributes
78    ----------
79    controlfile : str
80        The name of the control file to be processed. Default value is the
81        filename passed to the init function when initialised.
82
83    start_date : str
84        The first day of the retrieval period. Default value is None.
85
86    end_date :str
87        The last day of the retrieval period. Default value is None.
88
89    date_chunk : int
90        Length of period for a single mars retrieval. Default value is 3.
91
92    dtime :str
93        The time step in hours. Default value is None.
94
95    basetime : str
96        The time for a half day retrieval. The 12 hours upfront are to be
97        retrieved. Default value is None.
98
99    maxstep : int
100        The maximum forecast step for non flux data. Default value is None.
101
102    type : list of str
103        List of field type per retrieving hour. Default value is None.
104
105    time : list of str
106        List of retrieving times in hours. Default valuer is None.
107
108    step : list of str or str
109        List of forecast time steps in hours for non flux data.
110        Default value is None.
111
112    acctype : str
113        The field type for the accumulated forecast fields.
114        Default value is None.
115
116    acctime : str
117        The starting time of the accumulated forecasts. Default value is None.
118
119    accmaxstep : int
120        The maximum forecast step for the accumulated forecast fields
121        (flux data). Default value is None.
122
123    marsclass : str
124        Characterisation of dataset. Default value is None.
125
126    dataset : str
127        For public datasets there is the specific naming and parameter
128        dataset which has to be used to characterize the type of
129        data. Default value is None.
130
131    stream : str
132        Identifies the forecasting system used to generate the data.
133        Default value is None.
134
135    number : str
136        Selects the member in ensemble forecast run. Default value is 'OFF'.
137
138    expver : str
139        The version number of the dataset. Default value is '1'.
140
141    gaussian : str
142        This parameter is deprecated and should no longer be used.
143        Specifies the desired type of Gaussian grid for the output.
144        Default value is an empty string ''.
145
146    grid : str
147        Specifies the output grid which can be either a Gaussian grid
148        or a Latitude/Longitude grid. Default value is None.
149
150    area : str
151        Specifies the desired sub-area of data to be extracted.
152        Default value is None.
153
154    left : str
155        The western most longitude of the area to be extracted.
156        Default value is None.
157
158    lower : str
159        The southern most latitude of the area to be extracted.
160        Default value is None.
161
162    upper : str
163        The northern most latitued of the area to be extracted.
164        Default value is None.
165
166    right : str
167        The eastern most longitude of the area to be extracted.
168        Default value is None.
169
170    level : str
171        Specifies the maximum level. Default value is None.
172
173    levelist : str
174        Specifies the required level list. Default value is None.
175
176    resol : str
177        Specifies the desired triangular truncation of retrieved data,
178        before carrying out any other selected post-processing.
179        Default value is None.
180
181    gauss : int
182        Switch to select gaussian fields (1) or regular lat/lon (0).
183        Default value is 0.
184
185    accuracy : int
186        Specifies the number of bits per value to be used in the
187        generated GRIB coded fields. Default value is 24.
188
189    omega : int
190       Switch to select omega retrieval (1) or not (0). Default value is 0.
191
192    omegadiff : int
193        Switch to decide to calculate Omega and Dps/Dt from continuity
194        equation for diagnostic purposes (1) or not (0). Default value is 0.
195
196    eta : int
197        Switch to select direct retrieval of etadot from MARS (1) or
198        wether it has to be calculated (0). Then Default value is 0.
199
200    etadiff : int
201        Switch to select calculation of etadot and Dps/Dt from continuity
202        equation for diagnostic purposes (1) or not (0). Default value is 0.
203
204    etapar : int
205        GRIB parameter Id for etadot fields. Default value is 77.
206
207    dpdeta : int
208        Switch to select multiplication of etadot with dpdeta.
209        Default value is 1.
210
211    smooth : int
212        Spectral truncation of ETADOT after calculation on Gaussian grid.
213        Default value is 0.
214
215    format : str
216        The format of the GRIB data. Default value is 'GRIB1'.
217
218    addpar : str
219        List of additional surface level ECMWF parameter to be retrieved.
220        Default value is None.
221
222    prefix : str
223        Prefix string for the final FLEXPART/FLEXTRA ready input files.
224        Default value is 'EN'.
225
226    cwc : int
227        Switch to select wether the sum of cloud liquid water content and
228        cloud ice water content should be retrieved. Default value is 0.
229
230    wrf : int
231        Switch to select further parameters for retrievment to support
232        WRF simulations. Default value is 0.
233
234    ecfsdir : str
235        Path to the ECMWF storage  'ectmp:/${USER}/econdemand/'
236
237    mailfail : list of str
238        Email list for sending error log files from ECMWF servers.
239        The email addresses should be seperated by a comma.
240        Default value is ['${USER}'].
241
242    mailops : list of str
243        Email list for sending operational log files from ECMWF servers.
244        The email addresses should be seperated by a comma.
245        Default value is ['${USER}'].
246
247    grib2flexpart : int 0
248        Switch to select generation of preprocessed FLEXPART files ".fp".
249        If it is selected, the program grib2flexpart will try
250        to convert the flex_extract output files into ".fp" format.
251
252    ecstorage : int
253        Switch to select storage of FLEXPART ready output files
254        in the ECFS file system. Default value is 0.
255
256    ectrans : int
257        Switch to select the transfer of FLEXPART ready output files
258        to the gateway server. Default value is 0.
259
260    inputdir : str
261        Path to the temporary directory for the retrieval grib files and
262        other processing files. Default value is _config.PATH_INPUT_DIR.
263
264    outputdir : str
265        Path to the final directory where the final FLEXPART ready input
266        files are stored. Default value is None.
267
268    flexextractdir : str
269        Path to the flex_extract root directory. Default value is
270        _config.PATH_FLEXEXTRACT_DIR.
271
272    exedir : str
273        Path to the FORTRAN executable file. Default value is
274        _config.PATH_FORTRAN_SRC.
275
276    flexpartdir : str
277        Path to a FLEXPART root directory. Default value is None.
278
279    makefile : str
280        Name of the makefile to be used for the Fortran program.
281        Default value is 'Makefile.gfortran'.
282
283    destination : str
284        The remote destination which is used to transfer files
285        from ECMWF server to local gateway server. Default value is None.
286
287    gateway : str
288        The gateway server the user is using. Default value is None.
289
290    ecuid : str
291        The user id on ECMWF server. Default value is None.
292
293    ecgid : str
294        The group id on ECMWF server. Default value is None.
295
296    install_target : str
297        Defines the location where the installation is to be done.
298        Default value is None.
299
300    debug : int
301        Switch to keep temporary files at the end of postprocessing (1) or
302        to delete all temporary files except the final output files (0).
303        Default value is 0.
304
305    request : int
306        Switch to select between just retrieving the data (0), writing the mars
307        parameter values to a csv file (1) or doing both (2).
308        Default value is 0.
309
310    public : int
311        Switch to select kind of ECMWF Web Api access and the
312        possible data sets. Public data sets (1) and Memberstate data sets (0).
313        Default value is 0.
314
315    ecapi : boolean
316        Tells wether the ECMWF Web APi was able to load or not.
317        Default value is None.
318
319    purefc : int
320        Switch to decide wether the job is a pure forecast retrieval or
321        coupled with analysis data. Default value is 0.
322
323    rrint: int
324        Switch to select between old precipitation disaggregation method (0)
325        or the new IA3 disaggegration method (1). Default value is 0.
326
327    logicals : list of str
328        List of the names of logical switches which controls the flow
329        of the program. Default list is ['gauss', 'omega', 'omegadiff', 'eta',
330        'etadiff', 'dpdeta', 'cwc', 'wrf', 'grib2flexpart', 'ecstorage',
331        'ectrans', 'debug', 'request', 'public', 'purefc', 'rrint']
[812283d]332    '''
333
334    def __init__(self, filename):
[274f9ef]335        '''Initialises the instance of ControlFile class and defines
336        all class attributes with default values. Afterwards calls
337        function __read_controlfile__ to read parameter from Control file.
[812283d]338
[274f9ef]339        Parameters
340        ----------
[6f951ca]341        filename : str
[274f9ef]342            Name of CONTROL file.
[812283d]343
[274f9ef]344        Return
345        ------
[812283d]346
347        '''
[54a8a01]348
349        # list of all possible class attributes and their default values
350        self.controlfile = filename
351        self.start_date = None
352        self.end_date = None
353        self.date_chunk = 3
[6f951ca]354        self.job_chunk = None
[54a8a01]355        self.dtime = None
356        self.basetime = None
357        self.maxstep = None
358        self.type = None
359        self.time = None
360        self.step = None
[0e576fc]361        self.acctype = None
362        self.acctime = None
363        self.accmaxstep = None
[54a8a01]364        self.marsclass = None
[5bad6ec]365        self.dataset = None
[54a8a01]366        self.stream = None
367        self.number = 'OFF'
[2fb99de]368        self.expver = '1'
[0e576fc]369        self.gaussian = ''
[54a8a01]370        self.grid = None
371        self.area = ''
372        self.left = None
373        self.lower = None
374        self.upper = None
375        self.right = None
376        self.level = None
377        self.levelist = None
378        self.resol = None
379        self.gauss = 0
380        self.accuracy = 24
381        self.omega = 0
382        self.omegadiff = 0
383        self.eta = 0
384        self.etadiff = 0
385        self.etapar = 77
386        self.dpdeta = 1
387        self.smooth = 0
388        self.format = 'GRIB1'
389        self.addpar = None
390        self.prefix = 'EN'
391        self.cwc = 0
392        self.wrf = 0
393        self.ecfsdir = 'ectmp:/${USER}/econdemand/'
394        self.mailfail = ['${USER}']
395        self.mailops = ['${USER}']
396        self.grib2flexpart = 0
397        self.ecstorage = 0
398        self.ectrans = 0
[25b14be]399        self.inputdir = _config.PATH_INPUT_DIR
[ca867de]400        self.outputdir = None
[145fbe0]401        self.flexextractdir = _config.PATH_FLEXEXTRACT_DIR
[2fb99de]402        self.exedir = _config.PATH_FORTRAN_SRC
[145fbe0]403        self.flexpartdir = None
[2fb99de]404        self.makefile = 'Makefile.gfortran'
[54a8a01]405        self.destination = None
406        self.gateway = None
407        self.ecuid = None
408        self.ecgid = None
409        self.install_target = None
410        self.debug = 0
[2fb99de]411        self.request = 0
[5bad6ec]412        self.public = 0
[96e1533]413        self.ecapi = None
[4d3b052]414        self.purefc = 0
[c274d9a]415        self.rrint = 0
[2fb99de]416
417        self.logicals = ['gauss', 'omega', 'omegadiff', 'eta', 'etadiff',
418                         'dpdeta', 'cwc', 'wrf', 'grib2flexpart', 'ecstorage',
[4d3b052]419                         'ectrans', 'debug', 'request', 'public', 'purefc',
420                         'rrint']
[54a8a01]421
[5c0a578]422        self._read_controlfile()
[54a8a01]423
424        return
425
[5c0a578]426    def _read_controlfile(self):
[274f9ef]427        '''Read CONTROL file and assign all CONTROL file variables.
[54a8a01]428
[274f9ef]429        Parameters
430        ----------
431
432        Return
433        ------
[54a8a01]434
435        '''
[4971f63]436
437        try:
[70fee58]438            cfile = os.path.join(_config.PATH_CONTROLFILES, self.controlfile)
439            with open(cfile) as f:
[4971f63]440                fdata = f.read().split('\n')
441        except IOError:
[70fee58]442            print('Could not read CONTROL file "' + cfile + '"')
[4971f63]443            print('Either it does not exist or its syntax is wrong.')
444            print('Try "' + sys.argv[0].split('/')[-1] + \
445                      ' -h" to print usage information')
446            sys.exit(1)
[812283d]447
448        # go through every line and store parameter
449        for ldata in fdata:
[87ae9a3]450            if ldata and ldata[0] == '#':
451                # ignore comment line in control file
452                continue
453            if '#' in ldata:
454                # cut off comment
455                ldata = ldata.split('#')[0]
[812283d]456            data = ldata.split()
457            if len(data) > 1:
458                if 'm_' in data[0].lower():
459                    data[0] = data[0][2:]
460                if data[0].lower() == 'class':
461                    data[0] = 'marsclass'
462                if data[0].lower() == 'day1':
463                    data[0] = 'start_date'
464                if data[0].lower() == 'day2':
465                    data[0] = 'end_date'
466                if len(data) == 2:
467                    if '$' in data[1]:
468                        setattr(self, data[0].lower(), data[1])
469                        while '$' in data[1]:
470                            i = data[1].index('$')
471                            j = data[1].find('{')
472                            k = data[1].find('}')
473                            var = os.getenv(data[1][j+1:k])
474                            if var is not None:
475                                data[1] = data[1][:i] + var + data[1][k+1:]
476                            else:
[54a8a01]477                                my_error(self.mailfail,
478                                         'Could not find variable '
479                                         + data[1][j+1:k] + ' while reading ' +
480                                         self.controlfile)
[812283d]481                        setattr(self, data[0].lower() + '_expanded', data[1])
482                    else:
483                        if data[1].lower() != 'none':
484                            setattr(self, data[0].lower(), data[1])
485                        else:
486                            setattr(self, data[0].lower(), None)
487                elif len(data) > 2:
488                    setattr(self, data[0].lower(), (data[1:]))
489            else:
490                pass
491
[54a8a01]492        return
493
494    def __str__(self):
[274f9ef]495        '''Prepares a string which have all the ControlFile class attributes
496        with its associated values. Each attribute is printed in one line and
497        in alphabetical order.
498
499        Example
500        -------
501        'age': 10
502        'color': 'Spotted'
503        'kids': 0
504        'legs': 2
505        'name': 'Dog'
506        'smell': 'Alot'
507
508        Parameters
509        ----------
510
511        Return
512        ------
513        string
514            Single string of concatenated ControlFile class attributes
515            with their values
[54a8a01]516        '''
517        import collections
518
[5bad6ec]519        attrs = vars(self).copy()
[54a8a01]520        attrs = collections.OrderedDict(sorted(attrs.items()))
521
522        return '\n'.join("%s: %s" % item for item in attrs.items())
523
524    def assign_args_to_control(self, args):
[274f9ef]525        '''Overwrites the existing ControlFile instance attributes with
526        the command line arguments.
[54a8a01]527
[274f9ef]528        Parameters
529        ----------
[6f951ca]530        args : Namespace
[274f9ef]531            Contains the commandline arguments from script/program call.
[54a8a01]532
[274f9ef]533        Return
534        ------
[54a8a01]535
536        '''
537
538        # get dictionary of command line parameters and eliminate all
539        # parameters which are None (were not specified)
540        args_dict = vars(args)
541        arguments = {k : args_dict[k] for k in args_dict
542                     if args_dict[k] != None}
543
544        # assign all passed command line arguments to ControlFile instance
545        for k, v in arguments.iteritems():
546            setattr(self, str(k), v)
547
548        return
549
550    def assign_envs_to_control(self, envs):
[274f9ef]551        '''Assigns the ECMWF environment parameter.
[54a8a01]552
[274f9ef]553        Parameters
554        ----------
[6f951ca]555        envs : dict of str
[274f9ef]556            Contains the ECMWF environment parameternames "ECUID", "ECGID",
557            "DESTINATION" and "GATEWAY" with its corresponding values.
558            They were read from the file "ECMWF_ENV".
559
560        Return
561        ------
[54a8a01]562
563        '''
564
565        for k, v in envs.iteritems():
566            setattr(self, str(k).lower(), str(v))
567
568        return
569
[2fb99de]570    def check_conditions(self, queue):
[274f9ef]571        '''Checks a couple of necessary attributes and conditions,
572        such as if they exist and contain values.
573        Otherwise set default values.
574
575        Parameters
576        ----------
[6f951ca]577        queue : str
[274f9ef]578            Name of the queue if submitted to the ECMWF servers.
579            Used to check if ecuid, ecgid, gateway and destination
580            are set correctly and are not empty.
581
582        Return
583        ------
584
[54a8a01]585        '''
[145fbe0]586        check_logicals_type(self, self.logicals)
587
588        self.mailfail = check_mail(self.mailfail)
589
590        self.mailops = check_mail(self.mailops)
[54a8a01]591
[145fbe0]592        check_queue(queue, self.gateway, self.destination,
593                    self.ecuid, self.ecgid)
[54a8a01]594
[145fbe0]595        self.outputdir, self.flexpartdir = check_pathes(self.inputdir,
596             self.outputdir, self.flexpartdir, self.flexextractdir)
597
598        self.start_date, self.end_date = check_dates(self.start_date,
599                                                     self.end_date)
600
601        check_basetime(self.basetime)
[ca867de]602
[3f36e42]603        self.levelist, self.level = check_levels(self.levelist, self.level)
604
[145fbe0]605        self.step = check_step(self.step, self.mailfail)
[812283d]606
[145fbe0]607        self.maxstep = check_maxstep(self.maxstep, self.step)
[812283d]608
[145fbe0]609        check_request(self.request,
610                      os.path.join(self.inputdir, _config.FILE_MARS_REQUESTS))
[5bad6ec]611
[145fbe0]612        check_public(self.public, self.dataset)
[0e576fc]613
[145fbe0]614        self.type = check_type(self.type, self.step)
[0e576fc]615
[145fbe0]616        self.time = check_time(self.time)
[0e576fc]617
[145fbe0]618        self.type, self.time, self.step = check_len_type_time_step(self.type,
619                                                                   self.time,
620                                                                   self.step,
621                                                                   self.maxstep,
622                                                                   self.purefc)
[0e576fc]623
[145fbe0]624        self.acctype = check_acctype(self.acctype, self.type)
[0e576fc]625
[145fbe0]626        self.acctime = check_acctime(self.acctime, self.acctype, self.purefc)
[0e576fc]627
[145fbe0]628        self.accmaxstep = check_accmaxstep(self.accmaxstep, self.acctype,
629                                           self.purefc, self.maxstep)
[0e576fc]630
[4d3b052]631        self.purefc = check_purefc(self.type)
632
[3f36e42]633        self.grid = check_grid(self.grid)
[97f4f4c]634
[3f36e42]635        self.area = check_area(self.grid, self.area, self.upper, self.lower,
636                               self.left, self.right)
[97f4f4c]637
[145fbe0]638        self.addpar = check_addpar(self.addpar)
639
[f2616a3]640        self.job_chunk = check_job_chunk(self.job_chunk)
[2d56c04]641
[812283d]642        return
643
[ff99eae]644    def to_list(self):
[274f9ef]645        '''Just generates a list of strings containing the attributes and
646        assigned values except the attributes "_expanded", "exedir",
[145fbe0]647        "flexextractdir" and "flexpartdir".
[274f9ef]648
649        Parameters
650        ----------
651
652        Return
653        ------
[6f951ca]654        l : list of *
[274f9ef]655            A sorted list of the all ControlFile class attributes with
656            their values except the attributes "_expanded", "exedir",
[145fbe0]657            "flexextractdir" and "flexpartdir".
[812283d]658        '''
659
[54a8a01]660        import collections
661
[5bad6ec]662        attrs = collections.OrderedDict(sorted(vars(self).copy().items()))
[54a8a01]663
[812283d]664        l = list()
665
666        for item in attrs.items():
667            if '_expanded' in item[0]:
668                pass
669            elif 'exedir' in item[0]:
670                pass
[145fbe0]671            elif 'flexpartdir' in item[0]:
[812283d]672                pass
[145fbe0]673            elif 'flexextractdir' in item[0]:
[812283d]674                pass
675            else:
[ff99eae]676                if isinstance(item[1], list):
[812283d]677                    stot = ''
678                    for s in item[1]:
679                        stot += s + ' '
680
[ff2a11c]681                    l.append("%s %s\n" % (item[0], stot))
[812283d]682                else:
[ff2a11c]683                    l.append("%s %s\n" % item)
[812283d]684
685        return sorted(l)
[ff99eae]686
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG