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

dev
Last change on this file since a676cf7 was a676cf7, checked in by Anne Tipka <anne.tipka@…>, 18 months ago

modifications and adjustments to dates and times for correctly retrieving analysis and forecast data in operational mode

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