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

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

diverse changes due to PEP8 style guide and eliminating grib2flexpart; removed unused parameter

  • 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"
[991df6a]12#        - moved the AgurmentParser into a seperate function
13#        - adatpted the function for the use in flex_extract
[ff99eae]14#        - renamed 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
[6f951ca]24#        - seperated get_mars_data function into several smaller pieces:
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#*******************************************************************************
[6f951ca]38'''This script extracts MARS data from ECMWF servers.
[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
46    * main - the main function of the script
[6f951ca]47    * get_mars_data - overall control of ECMWF data retrievment
48    * write_reqheader - writes the header into the mars_request file
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_retrievement - creates individual retrievals
53
54Type: get_mars_data.py --help
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
68# software specific classes and modules from flex_extract
[6f951ca]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():
[274f9ef]96    '''Controls the program to get data out of mars.
97
98    This is done if it is called directly from command line.
99    Then it also takes program call arguments and control file 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):
[274f9ef]116    '''Retrieves the EC data needed for a FLEXPART simulation.
117
118    Start and end dates for retrieval period is set. Retrievals
119    are divided into smaller periods if necessary and datechunk parameter
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:
[2fb99de]139        print("Retrieving EC data!")
[403cbf1]140    else:
141        if c.request == 1:
142            print("Printing mars requests!")
143        elif c.request == 2:
144            print("Retrieving EC 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):
167    '''Writes header with column names into mars request file.
168
169    Parameters
170    ----------
[6f951ca]171    marsfile : str
[631ba13]172        Path to the mars request file.
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):
[f20af73]190    '''Creates a server connection with available python API.
191
192    Which API is used depends on availability and the dataset to be retrieved.
193    The CDS API is used for ERA5 dataset no matter if the user is a member or
194    a public user. ECMWF WebAPI is used for all other available datasets.
[631ba13]195
196    Parameters
197    ----------
[6f951ca]198    c : ControlFile
[631ba13]199        Contains all the parameters of CONTROL file and
200        command line.
201
202    Return
203    ------
[f20af73]204    server : ECMWFDataServer, ECMWFService or Client
205        Connection to ECMWF server via python interface ECMWF WebAPI or CDS API.
[631ba13]206
207    '''
[f20af73]208    if cds_api and (c.marsclass.upper() == 'EA'):
209        server = cdsapi.Client()
210        c.ec_api = False
211    elif c.ec_api:
[5bad6ec]212        if c.public:
213            server = ecmwfapi.ECMWFDataServer()
214        else:
215            server = ecmwfapi.ECMWFService("mars")
[f20af73]216        c.cds_api = False
[d69b677]217    else:
218        server = False
219
[f20af73]220    print('Using ECMWF WebAPI: ' + str(c.ec_api))
221    print('Using CDS API: ' + str(c.cds_api))
[991df6a]222
[631ba13]223    return server
[54a8a01]224
225
[45b99e6]226def check_dates_for_nonflux_fc_times(types, times):
[0f89116]227    '''Checks if the time 18UTC corresponds to forecast field.
228
229    Parameters
230    ----------
231    types : list of str
232        List of field types.
233
234    times : list of str or str
235        The time in hours of the field.
236
237    Return
238    ------
239    True or False
240
[45b99e6]241    '''
[0f89116]242    for ty, ti in zip(types, times):
[45b99e6]243        if ty.upper() == 'FC' and int(ti) == 18:
244            return True
245    return False
246
247
[631ba13]248def mk_dates(c, fluxes):
249    '''Prepares start and end date depending on flux or non flux data.
[54a8a01]250
[631ba13]251    If forecast for maximum one day (upto 24h) are to be retrieved, then
252    collect accumulation data (flux data) with additional days in the
253    beginning and at the end (used for complete disaggregation of
254    original period)
[54a8a01]255
[631ba13]256    If forecast data longer than 24h are to be retrieved, then
257    collect accumulation data (flux data) with the exact start and end date
258    (disaggregation will be done for the exact time period with
259    boundary conditions)
260
261    Since for basetime the extraction contains the 12 hours upfront,
[0f89116]262    if basetime is 0, the starting date has to be the day before
[631ba13]263
264    Parameters
265    ----------
[6f951ca]266    c : ControlFile
[631ba13]267        Contains all the parameters of CONTROL file and
268        command line.
269
[6f951ca]270    fluxes : boolean, optional
[631ba13]271        Decides if the flux parameter settings are stored or
272        the rest of the parameter list.
273        Default value is False.
274
275    Return
276    ------
[6f951ca]277    start : datetime
[631ba13]278        The start date of the retrieving data set.
279
[6f951ca]280    end : datetime
[631ba13]281        The end date of the retrieving data set.
282
[6f951ca]283    chunk : datetime
[631ba13]284        Time period in days for one single mars retrieval.
285
286    '''
[ca867de]287    start = datetime.strptime(c.start_date, '%Y%m%d')
288    end = datetime.strptime(c.end_date, '%Y%m%d')
[631ba13]289    chunk = timedelta(days=int(c.date_chunk))
290
[d4696e0]291    if c.basetime == 0:
292        start = start - timedelta(days=1)
[efdb01a]293
[7e25255]294    if c.purefc and fluxes and c.maxstep < 24:
295        start = start - timedelta(days=1)
296        end = end + timedelta(days=1)
297
[d4696e0]298    if not c.purefc and fluxes and not c.basetime == 0:
[ca867de]299        start = start - timedelta(days=1)
[631ba13]300        end = end + timedelta(days=1)
[ca867de]301
[45b99e6]302    # if we have non-flux forecast data starting at 18 UTC
303    # we need to start retrieving data one day in advance
304    if not fluxes and check_dates_for_nonflux_fc_times(c.type, c.time):
305        start = start - timedelta(days=1)
306
[631ba13]307    return start, end, chunk
[54a8a01]308
[1d15e27]309def remove_old(pattern, inputdir):
[3f36e42]310    '''Deletes old retrieval files from current input directory
311    matching the pattern.
[54a8a01]312
[631ba13]313    Parameters
314    ----------
[6f951ca]315    pattern : str
[631ba13]316        The sub string pattern which identifies the files to be deleted.
[991df6a]317
[6f951ca]318    inputdir : str, optional
[1d15e27]319        Path to the directory where the retrieved data is stored.
320
[631ba13]321    Return
322    ------
[991df6a]323
[631ba13]324    '''
[1d15e27]325    print('... removing old content of ' + inputdir)
[631ba13]326
[1d15e27]327    tobecleaned = UioFiles(inputdir, pattern)
[631ba13]328    tobecleaned.delete_files()
[54a8a01]329
330    return
331
[631ba13]332
[54a8a01]333def do_retrievement(c, server, start, end, delta_t, fluxes=False):
[274f9ef]334    '''Divides the complete retrieval period in smaller chunks and
335    retrieves the data from MARS.
336
337    Parameters
338    ----------
[6f951ca]339    c : ControlFile
[274f9ef]340        Contains all the parameters of CONTROL file and
341        command line.
[54a8a01]342
[6f951ca]343    server : ECMWFService or ECMWFDataServer
[274f9ef]344            The server connection to ECMWF.
[54a8a01]345
[6f951ca]346    start : datetime
[274f9ef]347        The start date of the retrieval.
[54a8a01]348
[6f951ca]349    end : datetime
[274f9ef]350        The end date of the retrieval.
[54a8a01]351
[6f951ca]352    delta_t : datetime
[274f9ef]353        Delta_t + 1 is the maximal time period of a single
354        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,
367    # we only need to add datechunk - 1 days to retrieval
368    # for a period
[ca867de]369    delta_t_m1 = delta_t - timedelta(days=1)
[54a8a01]370
[efdb01a]371    day = start
372    while day <= end:
[54a8a01]373        flexpart = EcFlexpart(c, fluxes)
374        tmpday = day + delta_t_m1
[ff99eae]375        if tmpday < end:
376            dates = day.strftime("%Y%m%d") + "/to/" + \
377                    tmpday.strftime("%Y%m%d")
378        else:
379            dates = day.strftime("%Y%m%d") + "/to/" + \
380                    end.strftime("%Y%m%d")
[efdb01a]381
[2fb99de]382        print("... retrieve " + dates + " in dir " + c.inputdir)
[64cf353]383
[ff99eae]384        try:
[5bad6ec]385            flexpart.retrieve(server, dates, c.public, c.request, c.inputdir)
[ff99eae]386        except IOError:
[f20af73]387            my_error('MARS request failed')
[efdb01a]388
[54a8a01]389        day += delta_t
[64cf353]390
[efdb01a]391    return
[d69b677]392
393if __name__ == "__main__":
[991df6a]394    main()
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG