source: flex_extract.git/Source/Python/Classes/MarsRetrieval.py @ c77630a

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

BUGFIX: for python3-eccodes the file openings need to be in binary mode! added binary marker in filemode

  • Property mode set to 100644
File size: 20.3 KB
Line 
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#*******************************************************************************
4# @Author: Anne Fouilloux (University of Oslo)
5#
6# @Date: October 2014
7#
8# @Change History:
9#
10#   November 2015 - Leopold Haimberger (University of Vienna):
11#        - optimized display_info
12#        - optimized data_retrieve and seperate between python and shell
13#          script call
14#
15#   February 2018 - Anne Philipp (University of Vienna):
16#        - applied PEP8 style guide
17#        - added documentation
18#        - applied some minor modifications in programming style/structure
19#        - added writing of mars request attributes to a csv file
20#
21# @License:
22#    (C) Copyright 2014-2019.
23#    Anne Philipp, Leopold Haimberger
24#
25#    SPDX-License-Identifier: CC-BY-4.0
26#
27#    This work is licensed under the Creative Commons Attribution 4.0
28#    International License. To view a copy of this license, visit
29#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
30#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
31#*******************************************************************************
32
33# ------------------------------------------------------------------------------
34# MODULES
35# ------------------------------------------------------------------------------
36from __future__ import print_function
37
38import os
39import sys
40import subprocess
41import traceback
42
43# software specific classes and modules from flex_extract
44sys.path.append('../')
45import _config
46try:
47    ec_api = True
48    import ecmwfapi
49except ImportError:
50    ec_api = False
51
52try:
53    cds_api = True
54    import cdsapi
55except ImportError:
56    cds_api = False
57# ------------------------------------------------------------------------------
58# CLASS
59# ------------------------------------------------------------------------------
60class MarsRetrieval(object):
61    '''Specific syntax and content for submission of MARS retrievals.
62
63    A MARS revtrieval has a specific syntax with a selection of keywords and
64    their corresponding values. This class provides the necessary functions
65    by displaying the selected parameters and their values and the actual
66    retrievement of the data through a mars request or a Python web api
67    interface. The initialization already expects all the keyword values.
68
69    A description of MARS keywords/arguments and examples of their
70    values can be found here:
71    https://software.ecmwf.int/wiki/display/UDOC/\
72                   Identification+keywords#Identificationkeywords-class
73
74    Attributes
75    ----------
76    server : ECMWFService or ECMWFDataServer
77        This is the connection to the ECMWF data servers.
78
79    public : int
80        Decides which Web API Server version is used.
81
82    marsclass : str, optional
83        Characterisation of dataset.
84
85    dataset : str, optional
86        For public datasets there is the specific naming and parameter
87        dataset which has to be used to characterize the type of
88        data.
89
90    type : str, optional
91        Determines the type of fields to be retrieved.
92
93    levtype : str, optional
94        Denotes type of level.
95
96    levelist : str, optional
97        Specifies the required levels.
98
99    repres : str, optional
100        Selects the representation of the archived data.
101
102    date : str, optional
103        Specifies the Analysis date, the Forecast base date or
104        Observations date.
105
106    resol : str, optional
107        Specifies the desired triangular truncation of retrieved data,
108        before carrying out any other selected post-processing.
109
110    stream : str, optional
111        Identifies the forecasting system used to generate the data.
112
113    area : str, optional
114        Specifies the desired sub-area of data to be extracted.
115
116    time : str, optional
117        Specifies the time of the data in hours and minutes.
118
119    step : str, optional
120        Specifies the forecast time step from forecast base time.
121
122    expver : str, optional
123        The version of the dataset.
124
125    number : str, optional
126        Selects the member in ensemble forecast run.
127
128    accuracy : str, optional
129        Specifies the number of bits per value to be used in the
130        generated GRIB coded fields.
131
132    grid : str, optional
133        Specifies the output grid which can be either a Gaussian grid
134        or a Latitude/Longitude grid.
135
136    gaussian : str, optional
137        This parameter is deprecated and should no longer be used.
138        Specifies the desired type of Gaussian grid for the output.
139
140    target : str, optional
141        Specifies a file into which data is to be written after
142        retrieval or manipulation.
143
144    param : str, optional
145        Specifies the meteorological parameter.
146    '''
147
148    def __init__(self, server, public, marsclass="EA", dataset="", type="",
149                 levtype="", levelist="", repres="", date="", resol="",
150                 stream="", area="", time="", step="", expver="1",
151                 number="", accuracy="", grid="", gaussian="", target="",
152                 param=""):
153        '''Initialises the instance of the MarsRetrieval class and
154        defines and assigns a set of the necessary retrieval parameters
155        for the FLEXPART input data.
156        A description of MARS keywords/arguments, their dependencies
157        on each other and examples of their values can be found here:
158
159        https://software.ecmwf.int/wiki/display/UDOC/MARS+keywords
160
161        Parameters
162        ----------
163        server : ECMWFService or ECMWFDataServer
164            This is the connection to the ECMWF data servers.
165            It is needed for the pythonic access of ECMWF data.
166
167        public : int
168            Decides which Web API version is used:
169            0: member-state users and full archive access
170            1: public access and limited access to the public server and
171               datasets. Needs the parameter dataset.
172            Default is "0" and for member-state users.
173
174        marsclass : str, optional
175            Characterisation of dataset. E.g. EI (ERA-Interim),
176            E4 (ERA40), OD (Operational archive), EA (ERA5).
177            Default is the ERA5 dataset "EA".
178
179        dataset : str, optional
180            For public datasets there is the specific naming and parameter
181            dataset which has to be used to characterize the type of
182            data. Usually there is less data available, either in times,
183            domain or parameter.
184            Default is an empty string.
185
186        type : str, optional
187            Determines the type of fields to be retrieved.
188            Selects between observations, images or fields.
189            Examples for fields: Analysis (an), Forecast (fc),
190            Perturbed Forecast (pf), Control Forecast (cf) and so on.
191            Default is an empty string.
192
193        levtype : str, optional
194            Denotes type of level. Has a direct implication on valid
195            levelist values!
196            E.g. model level (ml), pressure level (pl), surface (sfc),
197            potential vorticity (pv), potential temperature (pt)
198            and depth (dp).
199            Default is an empty string.
200
201        levelist : str, optional
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            Default is an empty string.
206
207        repres : str, optional
208            Selects the representation of the archived data.
209            E.g. sh - spherical harmonics, gg - Gaussian grid,
210            ll - latitude/longitude, ...
211            Default is an empty string.
212
213        date : str, optional
214            Specifies the Analysis date, the Forecast base date or
215            Observations date. Valid formats are:
216            Absolute as YYYY-MM-DD or YYYYMMDD.
217            Default is an empty string.
218
219        resol : str, optional
220            Specifies the desired triangular truncation of retrieved data,
221            before carrying out any other selected post-processing.
222            The default is automatic truncation (auto), by which the lowest
223            resolution compatible with the value specified in grid is
224            automatically selected for the retrieval.
225            Users wanting to perform post-processing from full spectral
226            resolution should specify Archived Value (av).
227            The following are examples of existing resolutions found in
228            the archive: 63, 106, 159, 213, 255, 319, 399, 511, 799 or 1279.
229            This keyword has no meaning/effect if the archived data is
230            not in spherical harmonics representation.
231            The best selection can be found here:
232            https://software.ecmwf.int/wiki/display/UDOC/\
233                  Retrieve#Retrieve-Truncationbeforeinterpolation
234            Default is an empty string.
235
236        stream : str, optional
237            Identifies the forecasting system used to generate the data.
238            E.g. oper (Atmospheric model), enfo (Ensemble forecats), ...
239            Default is an empty string.
240
241        area : str, optional
242            Specifies the desired sub-area of data to be extracted.
243            Areas can be defined to wrap around the globe.
244
245            Latitude values must be given as signed numbers, with:
246                north latitudes (i.e. north of the equator)
247                    being positive (e.g: 40.5)
248                south latitutes (i.e. south of the equator)
249                    being negative (e.g: -50.5)
250            Longtitude values must be given as signed numbers, with:
251                east longitudes (i.e. east of the 0 degree meridian)
252                    being positive (e.g: 35.0)
253                west longitudes (i.e. west of the 0 degree meridian)
254                    being negative (e.g: -20.5)
255
256            E.g.: North/West/South/East
257            Default is an empty string.
258
259        time : str, optional
260            Specifies the time of the data in hours and minutes.
261            Valid values depend on the type of data: Analysis time,
262            Forecast base time or First guess verification time
263            (all usually at synoptic hours: 00, 06, 12 and 18 ).
264            Observation time (any combination in hours and minutes is valid,
265            subject to data availability in the archive).
266            The syntax is HHMM or HH:MM. If MM is omitted it defaults to 00.
267            Default is an empty string.
268
269        step : str, optional
270            Specifies the forecast time step from forecast base time.
271            Valid values are hours (HH) from forecast base time. It also
272            specifies the length of the forecast which verifies at
273            First Guess time.
274            E.g. 1/3/6-hourly
275            Default is an empty string.
276
277        expver : str, optional
278            The version of the dataset. Each experiment is assigned a
279            unique code (version). Production data is assigned 1 or 2,
280            and experimental data in Operations 11, 12 ,...
281            Research or Member State's experiments have a four letter
282            experiment identifier.
283            Default is "1".
284
285        number : str, optional
286            Selects the member in ensemble forecast run. (Only then it
287            is necessary.) It has a different meaning depending on
288            the type of data.
289            E.g. Perturbed Forecasts: specifies the Ensemble forecast member
290            Default is an empty string.
291
292        accuracy : str, optional
293            Specifies the number of bits per value to be used in the
294            generated GRIB coded fields.
295            A positive integer may be given to specify the preferred number
296            of bits per packed value. This must not be greater than the
297            number of bits normally used for a Fortran integer on the
298            processor handling the request (typically 32 or 64 bit).
299            Within a compute request the accuracy of the original fields
300            can be passed to the result field by specifying accuracy=av.
301            Default is an empty string.
302
303        grid : str, optional
304            Specifies the output grid which can be either a Gaussian grid
305            or a Latitude/Longitude grid. MARS requests specifying
306            grid=av will return the archived model grid.
307
308            Lat/Lon grid: The grid spacing needs to be an integer
309            fraction of 90 degrees e.g. grid = 0.5/0.5
310
311            Gaussian grid: specified by a letter denoting the type of
312            Gaussian grid followed by an integer (the grid number)
313            representing the number of lines between the Pole and Equator,
314            e.g.
315            grid = F160 - full (or regular) Gaussian grid with
316                   160 latitude lines between the pole and equator
317            grid = N320 - ECMWF original reduced Gaussian grid with
318                   320 latitude lines between the pole and equator,
319                   see Reduced Gaussian Grids for grid numbers used at ECMWF
320            grid = O640 - ECMWF octahedral (reduced) Gaussian grid with
321                   640 latitude lines between the pole and equator
322            Default is an empty string.
323
324        gaussian : str, optional
325            This parameter is deprecated and should no longer be used.
326            Specifies the desired type of Gaussian grid for the output.
327            Valid Gaussian grids are quasi-regular (reduced) or regular.
328            Keyword gaussian can only be specified together with
329            keyword grid. Gaussian without grid has no effect.
330            Default is an empty string.
331
332        target : str, optional
333            Specifies a file into which data is to be written after
334            retrieval or manipulation. Path names should always be
335            enclosed in double quotes. The MARS client supports automatic
336            generation of multiple target files using MARS keywords
337            enclosed in square brackets [ ].  If the environment variable
338            MARS_MULTITARGET_STRICT_FORMAT is set to 1 before calling mars,
339            the keyword values will be used in the filename as shown by
340            the ecCodes GRIB tool grib_ls -m, e.g. with
341            MARS_MULTITARGET_STRICT_FORMAT set to 1 the keywords time,
342            expver and param will be formatted as 0600, 0001 and 129.128
343            rather than 600, 1 and 129.
344            Default is an empty string.
345
346        param : str, optional
347            Specifies the meteorological parameter.
348            The list of meteorological parameters in MARS is extensive.
349            Their availability is directly related to their meteorological
350            meaning and, therefore, the rest of directives specified
351            in the MARS request.
352            Meteorological parameters can be specified by their
353            GRIB code (param=130), their mnemonic (param=t) or
354            full name (param=temperature).
355            The list of parameter should be seperated by a "/"-sign.
356            E.g. 130/131/133
357            Default is an empty string.
358
359        Return
360        ------
361
362        '''
363
364        self.server = server
365        self.public = public
366        self.marsclass = marsclass
367        self.dataset = dataset
368        self.type = type
369        self.levtype = levtype
370        self.levelist = levelist
371        self.repres = repres
372        self.date = date
373        self.resol = resol
374        self.stream = stream
375        self.area = area
376        self.time = time
377        self.step = step
378        self.expver = expver
379        self.number = number
380        self.accuracy = accuracy
381        self.grid = grid
382        self.gaussian = gaussian
383        self.target = target
384        self.param = param
385
386        return
387
388
389    def display_info(self):
390        '''Prints all class attributes and their values to the
391        standard output.
392
393        Parameters
394        ----------
395
396        Return
397        ------
398
399        '''
400        # Get all class attributes and their values as a dictionary
401        attrs = vars(self).copy()
402
403        # iterate through all attributes and print them
404        # with their corresponding values
405        for item in attrs.items():
406            if item[0] in ['server', 'public']:
407                pass
408            else:
409                print(item[0] + ': ' + str(item[1]))
410
411        return
412
413
414    def print_infodata_csv(self, inputdir, request_number):
415        '''Write all request parameter in alpabetical order into a "csv" file.
416
417        Parameters
418        ----------
419        inputdir : str
420            The path where all data from the retrievals are stored.
421
422        request_number : int
423            Number of mars requests for flux and non-flux data.
424
425        Return
426        ------
427
428        '''
429
430        # Get all class attributes and their values as a dictionary
431        attrs = vars(self).copy()
432        del attrs['server']
433        del attrs['public']
434
435        # open a file to store all requests to
436        with open(os.path.join(inputdir,
437                               _config.FILE_MARS_REQUESTS), 'a') as f:
438            f.write(str(request_number) + ', ')
439            f.write(', '.join(str(attrs[key])
440                              for key in sorted(attrs.keys())))
441            f.write('\n')
442
443        return
444
445    def data_retrieve(self):
446        '''Submits a MARS retrieval. Depending on the existence of
447        ECMWF Web-API or CDS API it is submitted via Python or a
448        subprocess in the Shell. The parameter for the mars retrieval
449        are taken from the defined class attributes.
450
451        Parameters
452        ----------
453
454        Return
455        ------
456
457        '''
458        # Get all class attributes and their values as a dictionary
459        attrs = vars(self).copy()
460
461        # eliminate unnecessary attributes from the dictionary attrs
462        del attrs['server']
463        del attrs['public']
464
465        # exchange parameter name for marsclass
466        mclass = attrs.get('marsclass')
467        del attrs['marsclass']
468        attrs['class'] = mclass
469
470        # prepare target variable as needed for the Web API or CDS API mode
471        # within the dictionary for full access
472        # as a single variable for public access
473        target = attrs.get('target')
474        if not int(self.public):
475            del attrs['target']
476        print('target: ' + target)
477
478        # find all keys without a value and convert all other values to strings
479        empty_keys = []
480        for key, value in attrs.items():
481            if value == '':
482                empty_keys.append(str(key))
483            else:
484                attrs[key] = str(value)
485
486        # delete all empty parameter from the dictionary
487        for key in empty_keys:
488            del attrs[key]
489
490#        attrs['ppengine'] = 'emos'
491
492        # MARS request via Python script
493        if self.server:
494            try:
495                if cds_api and isinstance(self.server, cdsapi.Client):
496                    print('RETRIEVE ERA5 WITH CDS API!')
497                    self.server.retrieve(_config.CDS_DATASET,
498                                         attrs, target)
499                elif ec_api and isinstance(self.server, ecmwfapi.ECMWFDataServer):
500                    print('RETRIEVE PUBLIC DATA (NOT ERA5)!')
501                    self.server.retrieve(attrs)
502                elif ec_api and isinstance(self.server, ecmwfapi.ECMWFService):
503                    print('EXECUTE NON-PUBLIC RETRIEVAL (NOT ERA5)!')
504                    self.server.execute(attrs, target)
505                else:
506                    print('ERROR:')
507                    print('No match for Web API instance!')
508                    raise IOError
509            except Exception as e:
510                print('\n\nMARS Request failed!')
511                print(e)
512                tb = sys.exc_info()[2]
513                print(traceback.format_exc())
514                sys.exit()
515
516        # MARS request via call in shell
517        else:
518            request_str = 'ret'
519            for key, value in attrs.items():
520                request_str = request_str + ',' + key + '=' + str(value)
521            request_str += ',target="' + target + '"'
522            p = subprocess.Popen(['mars'], #'-e'],
523                                 stdin=subprocess.PIPE,
524                                 stdout=subprocess.PIPE,
525                                 stderr=subprocess.PIPE,
526                                 bufsize=1)
527            pout = p.communicate(input=request_str.encode())[0]
528            print(pout.decode())
529
530            if 'Some errors reported' in pout.decode():
531                print('MARS Request failed - please check request')
532                raise IOError
533            elif os.stat(target).st_size == 0:
534                print('MARS Request returned no data - please check request')
535                raise IOError
536
537        return
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG