source: flex_extract.git/source/python/mods/checks.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: 23.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#*******************************************************************************
4# @Author: Anne Philipp (University of Vienna)
5#
6# @Date: November 2018
7#
8# @Change History:
9#
10# @License:
11#    (C) Copyright 2014-2019.
12#    Anne Philipp, Leopold Haimberger
13#
14#    This work is licensed under the Creative Commons Attribution 4.0
15#    International License. To view a copy of this license, visit
16#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
17#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
18#*******************************************************************************
19'''This module contains check methods for the CONTROL paramaters.
20'''
21
22# ------------------------------------------------------------------------------
23# MODULES
24# ------------------------------------------------------------------------------
25
26import os
27import sys
28import _config
29import exceptions
30from tools import my_error, silent_remove
31from datetime import datetime
32# ------------------------------------------------------------------------------
33# FUNCTIONS
34# ------------------------------------------------------------------------------
35
36def check_logicals_type(c, logicals):
37    '''Check that the logical variables have correct type integer.
38
39    Parameters
40    ----------
41    c : ControlFile
42        Contains all the parameters of CONTROL file and
43        command line.
44
45    logicals : list of (str or int)
46        Names of the switches that are used to control the flow of the
47        program.
48
49    Return
50    ------
51
52    '''
53
54    for var in logicals:
55        if not isinstance(getattr(c, var), int):
56            setattr(c, var, int(getattr(c, var)))
57
58    return
59
60def check_grid(grid):
61    '''Convert grid into correct Lat/Lon format. E.g. '0.5/0.5'
62
63    Checks on format of original grid. Wether it is in the order of 1000 or 1.
64    Convert to correct grid format and substitute into "Lat/Lon" format string.
65
66    Parameters
67    ----------
68    grid : str
69        Contains grid information
70
71    Return
72    ------
73    grid : str
74        Contains grid in format Lat/lon. E.g. 0.1/0.1
75    '''
76
77    if 'N' in grid:
78        return grid
79    if '/' in grid:
80        gridx, gridy = grid.split('/')
81        if gridx == gridy:
82            grid = gridx
83        else:
84            raise ValueError('GRID parameter contains two values '
85                             'which are unequal %s' (grid))
86    # determine grid format
87    if float(grid) / 100. >= 0.5:
88        # grid is defined in 1/1000 degrees; old format
89        grid = '{}/{}'.format(float(grid) / 1000.,
90                              float(grid) / 1000.)
91    elif float(grid) / 100. < 0.5:
92        # grid is defined in normal degree; new format
93        grid = '{}/{}'.format(float(grid), float(grid))
94
95    return grid
96
97def check_area(grid, area, upper, lower, left , right):
98    '''Defines the correct area string.
99
100    Checks on the format of the four area components. Wether it is of
101    the order of 1000 or 1. Also checks wether area was already set by command
102    line, then the four components are overwritten.
103    Convert to correct format of the order of magnitude "1" and sets the
104    area parameter (North/West/South/East).
105    E.g.: -5./20./10./10.
106
107    Parameters
108    ----------
109    grid : str
110        Contains grid information.
111
112    area : str
113        Contains area informtion.
114
115    upper : str
116        The northern most latitude.
117
118    lower : str
119        The souther most latitude.
120
121    left : str
122        The western most longitude.
123
124    right : str
125        The eastern most longiude.
126
127    Return
128    ------
129    grid : str
130        Contains grid in format Lat/lon. E.g. 0.1/0.1
131    '''
132    if 'N' in grid:  # Gaussian output grid
133        area = 'G'
134        return area
135
136    # if area was provided decompose area into its 4 components
137    if area:
138        components = area.split('/')
139        upper, left, lower, right = components
140
141    # determine area format
142    if ((abs(float(upper) / 10000.) >= 0.01 or float(upper) / 1000. == 0. ) and
143        (abs(float(lower) / 10000.) >= 0.01 or float(lower) / 1000. == 0. ) and
144        (abs(float(left) / 10000.) >= 0.01 or float(left) / 1000. == 0. ) and
145        (abs(float(right) / 10000.) >= 0.01 or float(right) / 1000. == 0.)):
146        # area is defined in 1/1000 degrees; old format
147        area = '{}/{}/{}/{}'.format(float(upper) / 1000.,
148                                    float(left) / 1000.,
149                                    float(lower) / 1000.,
150                                    float(right) / 1000.)
151    elif (abs(float(upper) / 10000.) < 0.05 and
152          abs(float(lower) / 10000.) < 0.05 and
153          abs(float(left) / 10000.) < 0.05 and
154          abs(float(right) / 10000.) < 0.05):
155        # area is already in new format
156        area = '{}/{}/{}/{}'.format(float(upper),
157                                    float(left),
158                                    float(lower),
159                                    float(right))
160    else:
161        raise ValueError('The area components have different '
162                         'formats (upper, lower, left, right): '
163                         '{}/{}/{}/{}'.format(str(upper), str(lower),
164                                              str(left) , str(right)))
165
166    return area
167
168def check_levels(levelist, level):
169    '''Defines correct level list and guarantees that the maximum level is
170    one of the available maximum levels.
171
172    Parameters
173    ----------
174    levelist : str
175        Specifies the level list.
176        Examples: model level: 1/to/137, pressure levels: 500/to/1000
177
178    level : str
179        Specifies the maximum level.
180
181    Return
182    ------
183    levelist : str
184        Specifies the required levels. It has to have a valid
185        correspondence to the selected levtype.
186        Examples: model level: 1/to/137, pressure levels: 500/to/1000
187
188    level : str
189        Specifies the maximum level. It has to be one of the
190        available maximum level number as contained in the variable
191        MAX_LEVEL_LIST in "_config". E.g. [16, 19, 31, 40, 50, 60, 62, 91, 137]
192
193    '''
194    # assure consistency of levelist and level
195    if not levelist and not level:
196        raise ValueError('ERROR: neither levelist nor level '
197                         'specified in CONTROL file')
198    elif not levelist and level:
199        levelist = '1/to/' + level
200    elif (levelist and not level) or \
201         (levelist[-1] != level[-1]):
202        level = levelist.split('/')[-1]
203    else:
204        pass
205
206    # check if max level is a valid level
207    if int(level) not in _config.MAX_LEVEL_LIST:
208        raise ValueError('ERROR: \n'
209                         'LEVEL must be the maximum level of a specified '
210                         'level list from ECMWF, e.g. {} \n'
211                         'Check parameter "LEVEL" or the max level of '
212                         '"LEVELIST"!'.format(str(_config.MAX_LEVEL_LIST)))
213
214    return levelist, level
215
216
217def check_ppid(c, ppid):
218    '''Sets the current PPID.
219
220    Parameters
221    ----------
222    c : ControlFile
223            Contains all the parameters of CONTROL file and
224            command line.
225
226    ppid : int or None
227        Contains the ppid number provided by the command line parameter
228        of is None otherwise.
229
230    Return
231    ------
232
233    '''
234
235    if not ppid:
236        c.ppid = str(os.getppid())
237    else:
238        c.ppid = ppid
239
240    return
241
242
243def check_purefc(ftype):
244    '''Check for a pure forecast mode.
245
246    Parameters
247    ----------
248    ftype : list of str
249        List of field types.
250
251    Return
252    ------
253    True or False:
254        True if pure forecasts are to be retrieved. False if there are
255        analysis fields in between.
256    '''
257
258    if 'AN' not in ftype and '4V' not in ftype:
259        # pure forecast
260        return 1
261
262    return 0
263
264
265def check_step(step, mailfail):
266    '''Checks on step format and convert into a list of steps.
267
268    If the steps were defined with "to" and "by" they are converted into
269    a list of steps. If the steps were set in a string, it is
270    converted into a list.
271
272    Parameters
273    ----------
274    step : list of str or str
275        Specifies the forecast time step from forecast base time.
276        Valid values are hours (HH) from forecast base time.
277
278    mailfail : list of str
279        Contains all email addresses which should be notified.
280        It might also contain just the ecmwf user name which will trigger
281        mailing to the associated email address for this user.
282
283    Return
284    ------
285    step : list of str
286        List of forecast steps in format e.g. [001, 002, ...]
287    '''
288
289    if '/' in step:
290        steps = step.split('/')
291        if 'to' in step.lower() and 'by' in step.lower():
292            ilist = np.arange(int(steps[0]),
293                              int(steps[2]) + 1,
294                              int(steps[4]))
295            step = ['{:0>3}'.format(i) for i in ilist]
296        elif 'to' in step.lower() and 'by' not in step.lower():
297            my_error(mailfail, step + ':\n' +
298                     'if "to" is used in steps parameter, '
299                     'please use "by" as well')
300        else:
301            step = steps
302
303    if not isinstance(step, list):
304        step = [step]
305
306    return step
307
308def check_type(ftype, steps):
309    '''Check if type variable is of type list and if analysis field has
310    forecast step 0.
311
312    Parameters
313    ----------
314    ftype : list of str or str
315        List of field types.
316
317    steps : str
318        Specifies the forecast time step from forecast base time.
319        Valid values are hours (HH) from forecast base time.
320
321    Return
322    ------
323    ftype : list of str
324        List of field types.
325    '''
326    if not isinstance(ftype, list):
327        ftype = [ftype]
328
329    for i, val in enumerate(ftype):
330        if ftype[i] == 'AN' and int(steps[i]) != 0:
331            print('Analysis retrievals must have STEP = 0 (now set to 0)')
332            ftype[i] = 0
333
334    return ftype
335
336def check_time(ftime):
337    '''Check if time variable is of type list. Otherwise convert to list.
338
339    Parameters
340    ----------
341    ftime : list of str or str
342        The time in hours of the field.
343
344    Return
345    ------
346    ftime : list of str
347        The time in hours of the field.
348    '''
349    if not isinstance(ftime, list):
350        ftime = [ftime]
351
352    return ftime
353
354def check_len_type_time_step(ftype, ftime, steps, maxstep, purefc):
355    '''Check if
356
357    Parameters
358    ----------
359    ftype : list of str
360        List of field types.
361
362    ftime : list of str or str
363        The time in hours of the field.
364
365    steps : str
366        Specifies the forecast time step from forecast base time.
367        Valid values are hours (HH) from forecast base time.
368
369    maxstep : int
370        The maximum forecast time step in hours from the forecast base time.
371        This is the maximum step for non flux (accumulated) forecast data.
372
373    purefc : int
374        Switch for definition of pure forecast mode or not.
375
376    Return
377    ------
378    ftype : list of str
379        List of field types.
380
381    ftime : list of str
382        The time in hours of the field.
383
384    steps : str
385        Specifies the forecast time step from forecast base time.
386        Valid values are hours (HH) from forecast base time.
387    '''
388    if not (len(ftype) == len(ftime) == len(steps)):
389        raise ValueError('ERROR: The number of field types, times and steps '
390                         'are not the same! Please check the setting in the '
391                         'CONTROL file!')
392
393    # if pure forecast is selected and only one field type/time is set
394    # prepare a complete list of type/time/step combination upto maxstep
395    if len(ftype) == 1 and purefc:
396        ftype = []
397        steps = []
398        ftime = []
399        for i in range(0, maxstep + 1):
400            ftype.append(ftype[0])
401            steps.append('{:0>3}'.format(i))
402            ftime.append(ftime[0])
403
404    return ftype, ftime, steps
405
406def check_mail(mail):
407    '''Check the string of mail addresses, seperate them and convert to a list.
408
409    Parameters
410    ----------
411    mail : list of str or str
412        Contains email addresses for notifications.
413        It might also contain just the ecmwf user name which will trigger
414        mailing to the associated email address for this user.
415
416    Return
417    ------
418    mail : list of str
419        Contains email addresses for notifications.
420        It might also contain just the ecmwf user name which will trigger
421        mailing to the associated email address for this user.
422
423    '''
424    if not isinstance(mail, list):
425        if ',' in mail:
426            mail = mail.split(',')
427        elif ' ' in mail:
428            mail = mail.split()
429        else:
430            mail = [mail]
431
432    return mail
433
434def check_queue(queue, gateway, destination, ecuid, ecgid):
435    '''Check if the necessary ECMWF parameters are set if the queue is
436    one of the QUEUES_LIST (in _config).
437
438    Parameters
439    ----------
440    queue : str
441        Name of the queue if submitted to the ECMWF servers.
442        Used to check if ecuid, ecgid, gateway and destination
443        are set correctly and are not empty.
444
445    gateway : str
446        The address of the gateway server.
447
448    destination : str
449        The name of the destination of the gateway server for data
450        transfer through ectrans. E.g. name@genericSftp
451
452    ecuid : str
453        ECMWF user id.
454
455    ecgid : str
456        ECMWF group id.
457
458    Return
459    ------
460
461    '''
462    if queue in _config.QUEUES_LIST and \
463        not gateway or not destination or \
464        not ecuid or not ecgid:
465        raise ValueError('\nEnvironment variables GATEWAY, DESTINATION, ECUID '
466                         'and ECGID were not set properly! \n '
467                         'Please check for existence of file "ECMWF_ENV" '
468                         'in the run directory!')
469    return
470
471def check_pathes(idir, odir, fpdir, fedir):
472    '''Check if output and flexpart pathes are set.
473
474    Parameters
475    ----------
476    idir : str
477        Path to the temporary directory for MARS retrieval data.
478
479    odir : str
480        Path to the final output directory where the FLEXPART input files
481        will be stored.
482
483    fpdir : str
484        Path to FLEXPART root directory.
485
486    fedir : str
487        Path to flex_extract root directory.
488
489    Return
490    ------
491    odir : str
492        Path to the final output directory where the FLEXPART input files
493        will be stored.
494
495    fpdir : str
496        Path to FLEXPART root directory.
497
498    '''
499    if not fpdir:
500        fpdir = fedir
501
502    if not odir:
503        odir = idir
504
505    return odir, fpdir
506
507def check_dates(start, end):
508    '''Checks if there is at least a start date for a one day retrieval.
509
510    Checks if end date lies after start date and end date is set.
511
512    Parameters
513    ----------
514    start : str
515        The start date of the retrieval job.
516
517    end : str
518        The end date of the retrieval job.
519
520    Return
521    ------
522    start : str
523        The start date of the retrieval job.
524
525    end : str
526        The end date of the retrieval job.
527
528    '''
529    # check for having at least a starting date
530    # otherwise program is not allowed to run
531    if not start:
532        raise ValueError('start_date was neither specified in command line nor '
533                         'in CONTROL file.\n'
534                         'Try "{} -h" to print usage information'
535                         .format(sys.argv[0].split('/')[-1]) )
536
537    # retrieve just one day if end_date isn't set
538    if not end:
539        end = start
540
541    dstart = datetime.strptime(start, '%Y%m%d')
542    dend = datetime.strptime(end, '%Y%m%d')
543    if dstart > dend:
544        raise ValueError('ERROR: Start date is after end date! \n'
545                         'Please adapt the dates in CONTROL file or '
546                         'command line! (start={}; end={})'.format(start, end))
547
548    return start, end
549
550def check_maxstep(maxstep, steps):
551    '''Convert maxstep into integer if it is already given. Otherwise, select
552    maxstep by going through the steps list.
553
554    Parameters
555    ----------
556    maxstep : str
557        The maximum forecast time step in hours from the forecast base time.
558        This is the maximum step for non flux (accumulated) forecast data.
559
560    steps : str
561        Specifies the forecast time step from forecast base time.
562        Valid values are hours (HH) from forecast base time.
563
564    Return
565    ------
566    maxstep : int
567        The maximum forecast time step in hours from the forecast base time.
568        This is the maximum step for non flux (accumulated) forecast data.
569
570    '''
571    # if maxstep wasn't provided
572    # search for it in the "step" parameter
573    if not maxstep:
574        maxstep = 0
575        for s in steps:
576            if int(s) > maxstep:
577                maxstep = int(s)
578    else:
579        maxstep = int(maxstep)
580
581    return maxstep
582
583def check_basetime(basetime):
584    '''Check if basetime is set and contains one of the two
585    possible values (0, 12).
586
587    Parameters
588    ----------
589    basetime : str
590        The time for a half day retrieval. The 12 hours upfront are to be
591        retrieved.
592
593    Return
594    ------
595
596    '''
597    if basetime:
598        if int(basetime) != 0 and int(basetime) != 12:
599            raise ValueError('ERROR: Basetime has an invalid value '
600                             '-> {}'.format(str(basetime)))
601    return
602
603def check_request(request, marsfile):
604    '''Check if there is an old mars request file and remove it.
605
606    Parameters
607    ----------
608    request : int
609        Selects the mode of retrieval.
610        0: Retrieves the data from ECMWF.
611        1: Prints the mars requests to an output file.
612        2: Retrieves the data and prints the mars request.
613
614    marsfile : str
615        Path to the mars request file.
616
617    Return
618    ------
619
620    '''
621    if request != 0:
622        if os.path.isfile(marsfile):
623            silent_remove(marsfile)
624    return
625
626def check_public(public, dataset):
627    '''Check wether the dataset parameter is set for a
628    public data set retrieval.
629
630    Parameters
631    ----------
632    public : int
633        Specifies if public data are to be retrieved or not.
634
635    dataset : str
636        Specific name which identifies the public dataset.
637
638    Return
639    ------
640
641    '''
642    if public and not dataset:
643        raise ValueError('ERROR: If public mars data wants to be retrieved, '
644                         'the "dataset"-parameter has to be set too!')
645    return
646
647def check_acctype(acctype, ftype):
648    '''Guarantees that the accumulation field type is set.
649
650    If not set, it is derivated as in the old method (TYPE[1]).
651
652    Parameters
653    ----------
654    acctype : str
655        The field type for the accumulated forecast fields.
656
657    ftype : list of str
658        List of field types.
659
660    Return
661    ------
662    acctype : str
663        The field type for the accumulated forecast fields.
664    '''
665    if not acctype:
666        print('... Control parameter ACCTYPE was not defined.')
667        try:
668            if len(ftype) == 1 and ftype[0] != 'AN':
669                print('Use same field type as for the non-flux fields.')
670                acctype = ftype[0]
671            elif len(ftype) > 1 and ftype[1] != 'AN':
672                print('Use old setting by using TYPE[1] for flux forecast!')
673                acctype = ftype[1]
674        except:
675            raise ValueError('ERROR: Accumulation field type could not be set!')
676    else:
677        if acctype.upper() == 'AN':
678            raise ValueError('ERROR: Accumulation forecast fields can not be '
679                             'of type "analysis"!')
680    return acctype
681
682
683def check_acctime(acctime, acctype, purefc):
684    '''Guarantees that the accumulation forecast times were set.
685
686    If it is not set, it is tried to set the value fore some of the
687    most commonly used data sets. Otherwise it raises an error.
688
689    Parameters
690    ----------
691    acctime : str
692        The starting time from the accumulated forecasts.
693
694    acctype : str
695        The field type for the accumulated forecast fields.
696
697    purefc : int
698        Switch for definition of pure forecast mode or not.
699
700    Return
701    ------
702    acctime : str
703        The starting time from the accumulated forecasts.
704    '''
705    if not acctime:
706        print('... Control parameter ACCTIME was not defined.')
707        print('... Value will be set depending on field type:\n '
708              '\t\t EA=06/18\n\t\t EI/OD=00/12\n\t\t  EP=18')
709        if acctype.upper() == 'EA': # Era 5
710            acctime = '06/18'
711        elif acctype.upper() == 'EI': # Era-Interim
712            acctime = '00/12'
713        elif acctype.upper() == 'EP': # CERA
714            acctime = '18'
715        elif acctype.upper() == 'OD' and not purefc: # On-demand operational
716            acctime = '00/12'
717        else:
718            raise ValueError('ERROR: Accumulation forecast time can not '
719                             'automatically be derived!')
720    return acctime
721
722def check_accmaxstep(accmaxstep, acctype, purefc, maxstep):
723    '''Guarantees that the accumulation forecast step were set.
724
725    Parameters
726    ----------
727    accmaxstep : str
728        The maximum forecast step for the accumulated forecast fields.
729
730    acctype : str
731        The field type for the accumulated forecast fields.
732
733    purefc : int
734        Switch for definition of pure forecast mode or not.
735
736    maxstep : str
737        The maximum forecast time step in hours from the forecast base time.
738        This is the maximum step for non flux (accumulated) forecast data.
739
740    Return
741    ------
742    accmaxstep : str
743        The maximum forecast step for the accumulated forecast fields.
744    '''
745    if not accmaxstep:
746        print('... Control parameter ACCMAXSTEP was not defined.')
747        print('... Value will be set depending on field type/time: '
748              '\t\t EA/EI/OD=12\n\t\t  EP=24')
749        if acctype.upper() in ['EA', 'EI', 'OD'] and not purefc:
750            # Era 5, Era-Interim, On-demand operational
751            accmaxstep = '12'
752        elif acctype.upper() == 'EP': # CERA
753            accmaxstep = '18'
754        elif purefc and accmaxstep != maxstep:
755            accmaxstep = maxstep
756            print('... For pure forecast mode, the accumulated forecast must '
757                  'have the same maxstep as the normal forecast fields!\n'
758                  '\t\t Accmaxstep was set to maxstep!')
759        else:
760            raise ValueError('ERROR: Accumulation forecast step can not '
761                             'automatically be derived!')
762    else:
763        if purefc and int(accmaxstep) != int(maxstep):
764            accmaxstep = maxstep
765            print('... For pure forecast mode, the accumulated forecast must '
766                          'have the same maxstep as the normal forecast fields!\n'
767                          '\t\t Accmaxstep was set to maxstep!')
768    return accmaxstep
769
770def check_addpar(addpar):
771    '''Check that addpar has correct format of additional parameters in
772    a single string, so that it can be easily appended to the hard coded
773    parameters that are retrieved in any case.
774
775    Parameters
776    ----------
777    addpar : str or list of str
778        List of additional parameters to be retrieved.
779
780    Return
781    ------
782    addpar : str
783        List of additional parameters to be retrieved.
784    '''
785
786    if addpar and isinstance(addpar, str):
787        if '/' in addpar:
788            parlist = addpar.split('/')
789            parlist = [p for p in parlist if p is not '']
790        else:
791            parlist = [addpar]
792
793        addpar = '/' + '/'.join(parlist)
794
795    return addpar
796
797
798def check_job_chunk(job_chunk):
799    '''Checks that if job chunk is set, the number is positive and non zero.
800
801    Parameters
802    ----------
803    job_chunk : int
804        The number of days for a single job script.
805
806    Return
807    ------
808    job_chunk : int
809        The number of days for a single job script.
810    '''
811    if not job_chunk:
812        return job_chunk
813
814    if job_chunk < 0:
815        raise ValueError('ERROR: The number of job chunk is negative!\n'
816                         'It has to be a positive number!')
817    elif job_chunk == 0:
818        job_chunk = None
819    else:
820        pass
821
822    return job_chunk
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG