source: flex_extract.git/source/python/classes/MarsRetrieval.py @ 274f9ef

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

Converted docstrings to numpy style and build first structure for sphinxdocumentation (incl API)

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