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

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

corrected date setting for operational retrieval for basetime 00

  • Property mode set to 100755
File size: 11.6 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:
[026b359]28#    (C) Copyright 2014-2020.
[6f951ca]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
[6a9dbfd]150    if c.public:
151        server = mk_server(c)
152    else:
153        server = False
[631ba13]154
155    # if data are to be retrieved, clean up any old grib files
156    if c.request == 0 or c.request == 2:
[1d15e27]157        remove_old('*grb', c.inputdir)
[631ba13]158
159    # --------------  flux data ------------------------------------------------
[45c6337]160    start, end, datechunk = mk_dates(c, fluxes=True)
[631ba13]161    do_retrievement(c, server, start, end, datechunk, fluxes=True)
162
163    # --------------  non flux data --------------------------------------------
[45c6337]164    start, end, datechunk = mk_dates(c, fluxes=False)
[631ba13]165    do_retrievement(c, server, start, end, datechunk, fluxes=False)
166
167    return
168
169def write_reqheader(marsfile):
[8209738]170    '''Writes header with column names into MARS request file.
[631ba13]171
172    Parameters
173    ----------
[6f951ca]174    marsfile : str
[8209738]175        Path to the MARS request file.
[631ba13]176
177    Return
178    ------
179
180    '''
181    MR = MarsRetrieval(None, None)
182    attrs = vars(MR).copy()
183    del attrs['server']
184    del attrs['public']
185    with open(marsfile, 'w') as f:
186        f.write('request_number' + ', ')
[79729d5]187        f.write(', '.join(str(key) for key in sorted(attrs.keys())))
[631ba13]188        f.write('\n')
189
190    return
191
192def mk_server(c):
[8209738]193    '''Creates a server connection with available Python API.
[f20af73]194
[8209738]195    The API selected depends on availability and the data set to be retrieved.
[d139f3e]196    The CDS API is used for ERA5 data, no matter whether the user is a
197    member-state or a public user.
[8209738]198    ECMWF WebAPI is used for all other available datasets.
[631ba13]199
200    Parameters
201    ----------
[6f951ca]202    c : ControlFile
[631ba13]203        Contains all the parameters of CONTROL file and
204        command line.
205
206    Return
207    ------
[f20af73]208    server : ECMWFDataServer, ECMWFService or Client
209        Connection to ECMWF server via python interface ECMWF WebAPI or CDS API.
[631ba13]210
211    '''
[f20af73]212    if cds_api and (c.marsclass.upper() == 'EA'):
213        server = cdsapi.Client()
214        c.ec_api = False
215    elif c.ec_api:
[5bad6ec]216        if c.public:
217            server = ecmwfapi.ECMWFDataServer()
218        else:
219            server = ecmwfapi.ECMWFService("mars")
[f20af73]220        c.cds_api = False
[d69b677]221    else:
222        server = False
223
[f20af73]224    print('Using ECMWF WebAPI: ' + str(c.ec_api))
225    print('Using CDS API: ' + str(c.cds_api))
[991df6a]226
[631ba13]227    return server
[54a8a01]228
229
[45b99e6]230def check_dates_for_nonflux_fc_times(types, times):
[0f89116]231    '''Checks if the time 18UTC corresponds to forecast field.
232
233    Parameters
234    ----------
235    types : list of str
236        List of field types.
237
238    times : list of str or str
239        The time in hours of the field.
240
241    Return
242    ------
243    True or False
244
[45b99e6]245    '''
[0f89116]246    for ty, ti in zip(types, times):
[45b99e6]247        if ty.upper() == 'FC' and int(ti) == 18:
248            return True
249    return False
250
251
[631ba13]252def mk_dates(c, fluxes):
[8209738]253    '''Prepares start and end date depending on flux or non-flux type of data.
[54a8a01]254
[8209738]255    If forecasts for a maximum of one day (24 h) are to be retrieved, then
[631ba13]256    collect accumulation data (flux data) with additional days in the
[8209738]257    beginning and at the end (needed for complete disaggregation of
[631ba13]258    original period)
[54a8a01]259
[8209738]260    If forecast data for more than +24 h are to be retrieved, then
[631ba13]261    collect accumulation data (flux data) with the exact start and end date
262    (disaggregation will be done for the exact time period with
263    boundary conditions)
264
265    Since for basetime the extraction contains the 12 hours upfront,
[0f89116]266    if basetime is 0, the starting date has to be the day before
[631ba13]267
268    Parameters
269    ----------
[6f951ca]270    c : ControlFile
[631ba13]271        Contains all the parameters of CONTROL file and
272        command line.
273
[6f951ca]274    fluxes : boolean, optional
[631ba13]275        Decides if the flux parameter settings are stored or
276        the rest of the parameter list.
277        Default value is False.
278
279    Return
280    ------
[6f951ca]281    start : datetime
[631ba13]282        The start date of the retrieving data set.
283
[6f951ca]284    end : datetime
[631ba13]285        The end date of the retrieving data set.
286
[6f951ca]287    chunk : datetime
[631ba13]288        Time period in days for one single mars retrieval.
289
290    '''
[ca867de]291    start = datetime.strptime(c.start_date, '%Y%m%d')
292    end = datetime.strptime(c.end_date, '%Y%m%d')
[631ba13]293    chunk = timedelta(days=int(c.date_chunk))
294
[365c82a]295    if c.basetime == 0 and not fluxes:  # non-fluxes
[d4696e0]296        start = start - timedelta(days=1)
[efdb01a]297
[7e25255]298    if c.purefc and fluxes and c.maxstep < 24:
299        start = start - timedelta(days=1)
[d139f3e]300        if not c.oper:
[284ae90]301            end = end + timedelta(days=1)
[7e25255]302
[284ae90]303    if not c.purefc and fluxes:
[ca867de]304        start = start - timedelta(days=1)
[d139f3e]305        if not c.oper:
[284ae90]306            end = end + timedelta(days=1)
307        elif c.oper and c.basetime == 0:
308            end = end - timedelta(days=1)
[ca867de]309
[45b99e6]310    # if we have non-flux forecast data starting at 18 UTC
311    # we need to start retrieving data one day in advance
312    if not fluxes and check_dates_for_nonflux_fc_times(c.type, c.time):
313        start = start - timedelta(days=1)
314
[631ba13]315    return start, end, chunk
[54a8a01]316
[1d15e27]317def remove_old(pattern, inputdir):
[3f36e42]318    '''Deletes old retrieval files from current input directory
319    matching the pattern.
[54a8a01]320
[631ba13]321    Parameters
322    ----------
[6f951ca]323    pattern : str
[8209738]324        The substring pattern which identifies the files to be deleted.
[991df6a]325
[6f951ca]326    inputdir : str, optional
[8209738]327        Path to the directory where the retrieved data are stored.
[1d15e27]328
[631ba13]329    Return
330    ------
[991df6a]331
[631ba13]332    '''
[8209738]333    print('... removing old files in ' + inputdir)
[631ba13]334
[1d15e27]335    tobecleaned = UioFiles(inputdir, pattern)
[631ba13]336    tobecleaned.delete_files()
[54a8a01]337
338    return
339
[631ba13]340
[54a8a01]341def do_retrievement(c, server, start, end, delta_t, fluxes=False):
[8209738]342    '''Divides the total retrieval period into smaller chunks and
[274f9ef]343    retrieves the data from MARS.
344
345    Parameters
346    ----------
[6f951ca]347    c : ControlFile
[274f9ef]348        Contains all the parameters of CONTROL file and
349        command line.
[54a8a01]350
[6f951ca]351    server : ECMWFService or ECMWFDataServer
[274f9ef]352            The server connection to ECMWF.
[54a8a01]353
[6f951ca]354    start : datetime
[274f9ef]355        The start date of the retrieval.
[54a8a01]356
[6f951ca]357    end : datetime
[274f9ef]358        The end date of the retrieval.
[54a8a01]359
[6f951ca]360    delta_t : datetime
[8209738]361        Delta_t + 1 is the maximum time period of a single retrieval.
[54a8a01]362
[6f951ca]363    fluxes : boolean, optional
[274f9ef]364        Decides if the flux parameters are to be retrieved or
365        the rest of the parameter list.
366        Default value is False.
[54a8a01]367
[274f9ef]368    Return
369    ------
[54a8a01]370
371    '''
372
373    # since actual day also counts as one day,
[8209738]374    # we only need to add datechunk - 1 days to retrieval for a period
[ca867de]375    delta_t_m1 = delta_t - timedelta(days=1)
[54a8a01]376
[efdb01a]377    day = start
378    while day <= end:
[54a8a01]379        flexpart = EcFlexpart(c, fluxes)
380        tmpday = day + delta_t_m1
[ff99eae]381        if tmpday < end:
382            dates = day.strftime("%Y%m%d") + "/to/" + \
383                    tmpday.strftime("%Y%m%d")
384        else:
385            dates = day.strftime("%Y%m%d") + "/to/" + \
386                    end.strftime("%Y%m%d")
[efdb01a]387
[2fb99de]388        print("... retrieve " + dates + " in dir " + c.inputdir)
[64cf353]389
[ff99eae]390        try:
[5bad6ec]391            flexpart.retrieve(server, dates, c.public, c.request, c.inputdir)
[ff99eae]392        except IOError:
[f20af73]393            my_error('MARS request failed')
[efdb01a]394
[54a8a01]395        day += delta_t
[64cf353]396
[efdb01a]397    return
[d69b677]398
399if __name__ == "__main__":
[991df6a]400    main()
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG