source: flex_extract.git/Source/Python/Mods/checks.py @ 44174de

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

commented out the WRF parts since they are still under construction and added License SPDX tags

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