source: flex_extract.git/Source/Python/Mods/get_mars_data.py @ 8209738

ctbtodev
Last change on this file since 8209738 was 8209738, checked in by anphi <anne.philipp@…>, 4 years ago

language corrections in comment sections and print commands

  • Property mode set to 100755
File size: 11.4 KB
RevLine 
[8463d78]1#!/usr/bin/env python3
[efdb01a]2# -*- coding: utf-8 -*-
[991df6a]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):
[ff99eae]11#        - moved the getEIdata program into a function "get_mars_data"
[8209738]12#        - moved the AgurmentParser into a separate function
13#        - adapted the function for use in flex_extract
14#        - renamed source file to get_mars_data
[991df6a]15#
16#    February 2018 - Anne Philipp (University of Vienna):
17#        - applied PEP8 style guide
18#        - added structured documentation
19#        - minor changes in programming style for consistence
20#        - added function main and moved function calls vom __main__ there
21#          (necessary for better documentation with docstrings for later
22#          online documentation)
23#        - use of UIFiles class for file selection and deletion
[8209738]24#        - separated get_mars_data function into several smaller pieces:
[6f951ca]25#          write_reqheader, mk_server, mk_dates, remove_old, do_retrievment
[991df6a]26#
27# @License:
[6f951ca]28#    (C) Copyright 2014-2019.
29#    Anne Philipp, Leopold Haimberger
[991df6a]30#
[44174de]31#    SPDX-License-Identifier: CC-BY-4.0
32#
[6f951ca]33#    This work is licensed under the Creative Commons Attribution 4.0
34#    International License. To view a copy of this license, visit
35#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
36#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
[991df6a]37#*******************************************************************************
[8209738]38'''This script extracts MARS data from ECMWF.
[96e1533]39
[6f951ca]40At first, the necessary parameters from command line and CONTROL files are
41extracted. They define the data set to be extracted from MARS.
[96e1533]42
43This file can also be imported as a module and contains the following
44functions:
45
[8209738]46    * main            - the main function of the script
47    * get_mars_data   - overall control of ECMWF data retrievment
[6f951ca]48    * write_reqheader - writes the header into the mars_request file
[8209738]49    * mk_server       - creates the server connection to ECMWF servers
50    * mk_dates        - defines the start and end date
51    * remove_old      - deletes old retrieved grib files
52    * do_retrieval    - creates individual retrievals
[6f951ca]53
[8209738]54Type get_mars_data.py --help
[6f951ca]55to get information about command line parameters.
56Read the documentation for usage instructions.
57'''
[efdb01a]58# ------------------------------------------------------------------------------
59# MODULES
60# ------------------------------------------------------------------------------
[7e25255]61from __future__ import print_function
62
[991df6a]63import os
64import sys
65import inspect
[ca867de]66from datetime import datetime, timedelta
[ff99eae]67
[8209738]68# software-specific classes and modules from flex_extract
69# add path to local main Python path for flex_extract to get full access
[70fee58]70sys.path.append(os.path.dirname(os.path.abspath(
71    inspect.getfile(inspect.currentframe()))) + '/../')
[0f89116]72# pylint: disable=wrong-import-position
[2fb99de]73import _config
[0f89116]74from Mods.tools import (setup_controldata, my_error, normal_exit, make_dir)
[ba99230]75from Classes.EcFlexpart import EcFlexpart
76from Classes.UioFiles import UioFiles
77from Classes.MarsRetrieval import MarsRetrieval
[0f89116]78# pylint: enable=wrong-import-position
79# pylint: disable=invalid-name
[ca867de]80try:
[f20af73]81    ec_api = True
[ca867de]82    import ecmwfapi
83except ImportError:
[f20af73]84    ec_api = False
85
86try:
87    cds_api = True
88    import cdsapi
89except ImportError:
90    cds_api = False
[0f89116]91# pylint: enable=invalid-name
[efdb01a]92# ------------------------------------------------------------------------------
93# FUNCTION
94# ------------------------------------------------------------------------------
[991df6a]95def main():
[8209738]96    '''Controls the program to retrieve data from MARS.
[274f9ef]97
[8209738]98    This is done if called directly from command line.
99    Then, arguments and control file are taken as input.
[295ff45]100
[274f9ef]101    Parameters
102    ----------
[991df6a]103
[274f9ef]104    Return
105    ------
[991df6a]106
107    '''
[54a8a01]108
[f20af73]109    c, _, _, _ = setup_controldata()
[ff99eae]110    get_mars_data(c)
[f20af73]111    normal_exit('Retrieving MARS data: Done!')
[d69b677]112
[991df6a]113    return
114
[ff99eae]115def get_mars_data(c):
[8209738]116    '''Retrieves the ECMWF data required for a FLEXPART simulation.
[274f9ef]117
[8209738]118    Start and end dates for retrieval period are set. Retrievals
119    are divided into shorter periods if necessary and if datechunk parameter
[274f9ef]120    is set.
121
122    Parameters
123    ----------
[6f951ca]124    c : ControlFile
[274f9ef]125        Contains all the parameters of CONTROL file and
126        command line.
127
128    Return
129    ------
130
[991df6a]131    '''
[f20af73]132    c.ec_api = ec_api
133    c.cds_api = cds_api
[d69b677]134
135    if not os.path.exists(c.inputdir):
[5bad6ec]136        make_dir(c.inputdir)
[991df6a]137
[403cbf1]138    if c.request == 0:
[8209738]139        print("Retrieving ECMWF data!")
[403cbf1]140    else:
141        if c.request == 1:
[8209738]142            print("Printing MARS requests!")
[403cbf1]143        elif c.request == 2:
[8209738]144            print("Retrieving ECMWF data and printing MARS request!")
[631ba13]145        write_reqheader(os.path.join(c.inputdir, _config.FILE_MARS_REQUESTS))
[2fb99de]146
147    print("start date %s " % (c.start_date))
148    print("end date %s " % (c.end_date))
[d69b677]149
[631ba13]150    server = mk_server(c)
151
152    # if data are to be retrieved, clean up any old grib files
153    if c.request == 0 or c.request == 2:
[1d15e27]154        remove_old('*grb', c.inputdir)
[631ba13]155
156    # --------------  flux data ------------------------------------------------
[45c6337]157    start, end, datechunk = mk_dates(c, fluxes=True)
[631ba13]158    do_retrievement(c, server, start, end, datechunk, fluxes=True)
159
160    # --------------  non flux data --------------------------------------------
[45c6337]161    start, end, datechunk = mk_dates(c, fluxes=False)
[631ba13]162    do_retrievement(c, server, start, end, datechunk, fluxes=False)
163
164    return
165
166def write_reqheader(marsfile):
[8209738]167    '''Writes header with column names into MARS request file.
[631ba13]168
169    Parameters
170    ----------
[6f951ca]171    marsfile : str
[8209738]172        Path to the MARS request file.
[631ba13]173
174    Return
175    ------
176
177    '''
178    MR = MarsRetrieval(None, None)
179    attrs = vars(MR).copy()
180    del attrs['server']
181    del attrs['public']
182    with open(marsfile, 'w') as f:
183        f.write('request_number' + ', ')
[79729d5]184        f.write(', '.join(str(key) for key in sorted(attrs.keys())))
[631ba13]185        f.write('\n')
186
187    return
188
189def mk_server(c):
[8209738]190    '''Creates a server connection with available Python API.
[f20af73]191
[8209738]192    The API selected depends on availability and the data set to be retrieved.
193    The CDS API is used for ERA5 data, no matter whether the user is a
194    member-state or a public user.
195    ECMWF WebAPI is used for all other available datasets.
[631ba13]196
197    Parameters
198    ----------
[6f951ca]199    c : ControlFile
[631ba13]200        Contains all the parameters of CONTROL file and
201        command line.
202
203    Return
204    ------
[f20af73]205    server : ECMWFDataServer, ECMWFService or Client
206        Connection to ECMWF server via python interface ECMWF WebAPI or CDS API.
[631ba13]207
208    '''
[f20af73]209    if cds_api and (c.marsclass.upper() == 'EA'):
210        server = cdsapi.Client()
211        c.ec_api = False
212    elif c.ec_api:
[5bad6ec]213        if c.public:
214            server = ecmwfapi.ECMWFDataServer()
215        else:
216            server = ecmwfapi.ECMWFService("mars")
[f20af73]217        c.cds_api = False
[d69b677]218    else:
219        server = False
220
[f20af73]221    print('Using ECMWF WebAPI: ' + str(c.ec_api))
222    print('Using CDS API: ' + str(c.cds_api))
[991df6a]223
[631ba13]224    return server
[54a8a01]225
226
[45b99e6]227def check_dates_for_nonflux_fc_times(types, times):
[0f89116]228    '''Checks if the time 18UTC corresponds to forecast field.
229
230    Parameters
231    ----------
232    types : list of str
233        List of field types.
234
235    times : list of str or str
236        The time in hours of the field.
237
238    Return
239    ------
240    True or False
241
[45b99e6]242    '''
[0f89116]243    for ty, ti in zip(types, times):
[45b99e6]244        if ty.upper() == 'FC' and int(ti) == 18:
245            return True
246    return False
247
248
[631ba13]249def mk_dates(c, fluxes):
[8209738]250    '''Prepares start and end date depending on flux or non-flux type of data.
[54a8a01]251
[8209738]252    If forecasts for a maximum of one day (24 h) are to be retrieved, then
[631ba13]253    collect accumulation data (flux data) with additional days in the
[8209738]254    beginning and at the end (needed for complete disaggregation of
[631ba13]255    original period)
[54a8a01]256
[8209738]257    If forecast data for more than +24 h are to be retrieved, then
[631ba13]258    collect accumulation data (flux data) with the exact start and end date
259    (disaggregation will be done for the exact time period with
260    boundary conditions)
261
262    Since for basetime the extraction contains the 12 hours upfront,
[0f89116]263    if basetime is 0, the starting date has to be the day before
[631ba13]264
265    Parameters
266    ----------
[6f951ca]267    c : ControlFile
[631ba13]268        Contains all the parameters of CONTROL file and
269        command line.
270
[6f951ca]271    fluxes : boolean, optional
[631ba13]272        Decides if the flux parameter settings are stored or
273        the rest of the parameter list.
274        Default value is False.
275
276    Return
277    ------
[6f951ca]278    start : datetime
[631ba13]279        The start date of the retrieving data set.
280
[6f951ca]281    end : datetime
[631ba13]282        The end date of the retrieving data set.
283
[6f951ca]284    chunk : datetime
[631ba13]285        Time period in days for one single mars retrieval.
286
287    '''
[ca867de]288    start = datetime.strptime(c.start_date, '%Y%m%d')
289    end = datetime.strptime(c.end_date, '%Y%m%d')
[631ba13]290    chunk = timedelta(days=int(c.date_chunk))
291
[d4696e0]292    if c.basetime == 0:
293        start = start - timedelta(days=1)
[efdb01a]294
[7e25255]295    if c.purefc and fluxes and c.maxstep < 24:
296        start = start - timedelta(days=1)
297        end = end + timedelta(days=1)
298
[d4696e0]299    if not c.purefc and fluxes and not c.basetime == 0:
[ca867de]300        start = start - timedelta(days=1)
[631ba13]301        end = end + timedelta(days=1)
[ca867de]302
[45b99e6]303    # if we have non-flux forecast data starting at 18 UTC
304    # we need to start retrieving data one day in advance
305    if not fluxes and check_dates_for_nonflux_fc_times(c.type, c.time):
306        start = start - timedelta(days=1)
307
[631ba13]308    return start, end, chunk
[54a8a01]309
[1d15e27]310def remove_old(pattern, inputdir):
[3f36e42]311    '''Deletes old retrieval files from current input directory
312    matching the pattern.
[54a8a01]313
[631ba13]314    Parameters
315    ----------
[6f951ca]316    pattern : str
[8209738]317        The substring pattern which identifies the files to be deleted.
[991df6a]318
[6f951ca]319    inputdir : str, optional
[8209738]320        Path to the directory where the retrieved data are stored.
[1d15e27]321
[631ba13]322    Return
323    ------
[991df6a]324
[631ba13]325    '''
[8209738]326    print('... removing old files in ' + inputdir)
[631ba13]327
[1d15e27]328    tobecleaned = UioFiles(inputdir, pattern)
[631ba13]329    tobecleaned.delete_files()
[54a8a01]330
331    return
332
[631ba13]333
[54a8a01]334def do_retrievement(c, server, start, end, delta_t, fluxes=False):
[8209738]335    '''Divides the total retrieval period into smaller chunks and
[274f9ef]336    retrieves the data from MARS.
337
338    Parameters
339    ----------
[6f951ca]340    c : ControlFile
[274f9ef]341        Contains all the parameters of CONTROL file and
342        command line.
[54a8a01]343
[6f951ca]344    server : ECMWFService or ECMWFDataServer
[274f9ef]345            The server connection to ECMWF.
[54a8a01]346
[6f951ca]347    start : datetime
[274f9ef]348        The start date of the retrieval.
[54a8a01]349
[6f951ca]350    end : datetime
[274f9ef]351        The end date of the retrieval.
[54a8a01]352
[6f951ca]353    delta_t : datetime
[8209738]354        Delta_t + 1 is the maximum time period of a single retrieval.
[54a8a01]355
[6f951ca]356    fluxes : boolean, optional
[274f9ef]357        Decides if the flux parameters are to be retrieved or
358        the rest of the parameter list.
359        Default value is False.
[54a8a01]360
[274f9ef]361    Return
362    ------
[54a8a01]363
364    '''
365
366    # since actual day also counts as one day,
[8209738]367    # we only need to add datechunk - 1 days to retrieval for a period
[ca867de]368    delta_t_m1 = delta_t - timedelta(days=1)
[54a8a01]369
[efdb01a]370    day = start
371    while day <= end:
[54a8a01]372        flexpart = EcFlexpart(c, fluxes)
373        tmpday = day + delta_t_m1
[ff99eae]374        if tmpday < end:
375            dates = day.strftime("%Y%m%d") + "/to/" + \
376                    tmpday.strftime("%Y%m%d")
377        else:
378            dates = day.strftime("%Y%m%d") + "/to/" + \
379                    end.strftime("%Y%m%d")
[efdb01a]380
[2fb99de]381        print("... retrieve " + dates + " in dir " + c.inputdir)
[64cf353]382
[ff99eae]383        try:
[5bad6ec]384            flexpart.retrieve(server, dates, c.public, c.request, c.inputdir)
[ff99eae]385        except IOError:
[f20af73]386            my_error('MARS request failed')
[efdb01a]387
[54a8a01]388        day += delta_t
[64cf353]389
[efdb01a]390    return
[d69b677]391
392if __name__ == "__main__":
[991df6a]393    main()
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG