1919
2020class GetAnc_ecmwf :
2121
22- def timeStamp2yrMnthDayHrMinSec (timestamp ):
23- '''
24- Definition for timestamp managing
25- :param timeStamp: a string, the time in UTC with format yyyy-mm-ddThh:MM:ss
26- '''
27- date = timestamp .split ('T' )[0 ]
28- time = timestamp .split ('T' )[1 ]
29-
30- year = date .split ('-' )[0 ]
31- month = date .split ('-' )[1 ]
32- day = date .split ('-' )[2 ]
33-
34- hour = time .split (':' )[0 ]
35- minute = time .split (':' )[1 ]
36- second = time .split (':' )[2 ]
37-
38- return year ,month ,day ,hour ,minute ,second
39-
4022 def ECMWF_latLonTimeTags (lat , lon , timeStamp , latRes , lonRes , timeResHours ):
4123 '''
4224 :param timeStamp: a string, the time in UTC with format yyyy-mm-ddThh:MM:ss
4325 :param lat: a float, the query latitude in degrees North
4426 :param lon: a float, the query longitude in degrees East
27+ :param timeResHours: an integer, time resolution of the queried ECMWF dataset
4528 :return:
4629 latEff: a float, the effective latitude - i.e. to the closest resolution degree
4730 lonEff: a float, the effective latitude - i.e. to the closest resolution degree
4831 latLonTag: a string, a tag to indicate the effective lat/lon.
49- dateTag: a string, a tag to indicate time stamp, to the hour
32+ dateTagEff: a string, a tag to indicate effective date (i.e. date in UTC rounded to closest hour)
33+ timeStampEff: a string, a tag to indicate effective time (i.e. time in UTC rounded to closest hour)
5034 '''
5135
5236 # Lat-Lon
@@ -63,31 +47,19 @@ def ECMWF_latLonTimeTags(lat, lon, timeStamp, latRes, lonRes, timeResHours):
6347 str (int (np .sign (latEff ))).replace ('-1' , 'S' ).replace ('1' , 'N' ),
6448 np .abs (latEff * (10 ** latSigFigures )))
6549
66- # Time; choose 00:00 or 12:00 model
67- year , month , day , hour , minute , second = GetAnc_ecmwf .timeStamp2yrMnthDayHrMinSec (timeStamp )
68- dTtimeStamp = datetime .datetime (int (year ), int (month ), int (day ), int (hour ), int (minute ), int (second ),tzinfo = datetime .timezone .utc )
69- dTtimeVec = [datetime .datetime (int (year ), int (month ), int (day ), int (0 ), int (0 ), int (0 ),tzinfo = datetime .timezone .utc ),\
70- datetime .datetime (int (year ), int (month ), int (day ), int (12 ), int (0 ), int (0 ),tzinfo = datetime .timezone .utc )]
71- timeEff = min (dTtimeVec , key = lambda x : abs (x - dTtimeStamp ))
72-
73- # timeSec = datetime.datetime(int(year), int(month), int(day), int(hour), int(minute), int(second),tzinfo=datetime.timezone.utc).timestamp()
74- # timeEffSec = np.round(timeSec / (3600 * timeResHours)) * (3600 * timeResHours) - 7200
75- # timeStampEff = str(pd.Timestamp(datetime.datetime.fromtimestamp(timeEffSec))).replace(' ','T')
76- timeStampEff = str (pd .Timestamp (timeEff )).replace (' ' ,'T' )
77-
78- dateTag = timeStampEff .replace (':' ,'' ).replace ('-' ,'' ).replace ('T' ,'' )
79- # print(timeStamp)
80- # print(year, month, day, hour, minute, second)
81- # print(timeSec)
82- # print(timeEffSec)
83- # print(timeStampEff)
50+ # Convert to a datetime object
51+ epoch_time = datetime .datetime .strptime (':' .join (timeStamp .split (':' )[:- 2 ]), '%Y-%m-%dT%H:%M:%S' ).timestamp ()
52+ timeResHoursSecs = 3600 * timeResHours
53+ rounded_epoch_time = round (epoch_time / timeResHoursSecs ) * timeResHoursSecs
54+ rounded_timestamp = datetime .datetime .fromtimestamp (rounded_epoch_time ).strftime ('%Y-%m-%dT%H:%M:%S' )
55+ dateTagEff , timeStampEff = rounded_timestamp .split ('T' )
8456
85- return latEff ,lonEff ,timeStampEff , latLonTag ,dateTag
57+ return latEff ,lonEff ,latLonTag ,dateTagEff , timeStampEff
8658
8759
88- def EAC4_download_ensembles (lat , lon , timeStamp , EAC4_variables , pathOut ):
60+ def CAMS_download_ensembles (lat , lon , dateTag , timeTag , CAMS_variables , pathOut ):
8961 '''
90- Performs CDSAPI command to download the required data from EAC4 (dataset "cams-global-atmospheric-composition-forecasts") in netCDF
62+ Performs CDSAPI command to download the required data from CAMS (dataset "cams-global-atmospheric-composition-forecasts") in netCDF
9163 format. It will retrieve the variables in a single space-time point corresponding to
9264
9365 For more information, please check: https://ads.atmosphere.copernicus.eu/cdsapp#!/dataset/cams-global-atmospheric-composition-forecasts?tab=overview
@@ -96,38 +68,42 @@ def EAC4_download_ensembles(lat, lon, timeStamp, EAC4_variables, pathOut):
9668 :param lat: a float, the query latitude in degrees North
9769 :param lon: a float, the query longitude in degrees East
9870
99- :param EAC4_variables : a list, with the variables of interest
71+ :param CAMS_variables : a list, with the variables of interest
10072 :param pathOut: full path to output, a netCDF file with the requested variables.
10173 :return:
10274 '''
10375
10476 if os .path .exists (pathOut ):
10577 pass
10678 else :
107- print (f'Nearest model found at { timeStamp } ' )
10879 url ,key = read_user_credentials ('ECMWF_ADS' )
10980
110- year , month , day , hour , _ , _ = GetAnc_ecmwf .timeStamp2yrMnthDayHrMinSec (timeStamp )
81+ year = dateTag .split ('-' )[0 ]
82+ hour = timeTag .split (':' )[0 ]
11183
112- if int (year ) < 2003 :
113- print ('EAC4 dataset not available before 2003, skipping' )
84+ hourForecast = '%02d' % (int (hour ) // 12 )
85+ leadtime = str (int (hour ) % 12 )
86+
87+ if int (year ) < 2015 :
88+ print ('CAMS dataset not available before 2015, skipping' )
11489 else :
11590 try :
11691 c = cdsapi .Client (timeout = 5 , url = url , key = key )
11792 c .retrieve (
11893 'cams-global-atmospheric-composition-forecasts' ,
11994 {
120- 'format' : 'netcdf' ,
12195 'type' : 'forecast' ,
122- 'variable' : list (EAC4_variables .keys ()),
123- 'date' : '%s-%s-%s/%s-%s-%s' % (year , month , day , year , month , day ),
124- 'time' : '%s:00' % (hour ,),
96+ 'variable' : list (CAMS_variables .keys ()),
97+ 'date' : '%s/%s' % (dateTag , dateTag ),
12598 'area' : [lat , lon , lat , lon ],
126- 'leadtime_hour' : '0' ,
99+ 'time' : '%s:00' % hourForecast ,
100+ 'leadtime_hour' : leadtime ,
101+ 'format' : 'netcdf' ,
102+ 'download_format' : 'unarchived' ,
127103 },
128104 pathOut )
129105 except :
130- print ('EAC4 atmospheric data could not be retrieved. Check inputs.' )
106+ print ('CAMS atmospheric data could not be retrieved. Check inputs.' )
131107 exit ()
132108
133109
@@ -152,49 +128,49 @@ def get_ancillary_main(lat, lon, timeStamp, pathAncillary):
152128
153129 ancillary = {}
154130
155- #################### EAC4 ####################
156- pathEAC4 = pathAncillary
157- if not os .path .exists (pathEAC4 ):
158- os .mkdir (pathEAC4 )
131+ #################### CAMS ####################
132+ pathCAMS = pathAncillary
133+ if not os .path .exists (pathCAMS ):
134+ os .mkdir (pathCAMS )
159135
160- EAC4_variables = {
136+ CAMS_variables = {
161137 '10m_u_component_of_wind' :'u10' ,
162138 '10m_v_component_of_wind' :'v10' ,
163139 'total_aerosol_optical_depth_550nm' :'aod550'
164140 }
165141
166- EAC4nc = {}
142+ CAMSnc = {}
167143
168144
169145 # Check https://ads.atmosphere.copernicus.eu/cdsapp#!/dataset/cams-global-atmospheric-composition-forecasts?tab=overview
170146 latRes = 0.4
171147 lonRes = 0.4
172- timeResHours = 12
148+ timeResHours = 1
173149
174- latEff , lonEff , timeStampEff , latLonTag , dateTag = GetAnc_ecmwf .ECMWF_latLonTimeTags (lat , lon , timeStamp , latRes , lonRes , timeResHours )
150+ latEff , lonEff , latLonTag , dateTagEff , timeStampEff = GetAnc_ecmwf .ECMWF_latLonTimeTags (lat , lon , timeStamp , latRes , lonRes , timeResHours )
175151
176- pathOut = os .path .join (pathEAC4 , 'EAC4_ %s_%s.nc' % (latLonTag , dateTag ))
152+ pathOut = os .path .join (pathCAMS , 'CAMS_ %s_%s_% s.nc' % (latLonTag , dateTagEff . replace ( '-' , '' ), timeStampEff . replace ( ':' , '' ) ))
177153
178- GetAnc_ecmwf .EAC4_download_ensembles (latEff , lonEff , timeStampEff , EAC4_variables , pathOut )
154+ GetAnc_ecmwf .CAMS_download_ensembles (latEff , lonEff , dateTagEff , timeStampEff , CAMS_variables , pathOut )
179155
180156 try :
181- EAC4nc ['reanalysis' ] = xr .open_dataset (pathOut ,engine = 'netcdf4' )
182- EAC4_flag = True
157+ CAMSnc ['reanalysis' ] = xr .open_dataset (pathOut ,engine = 'netcdf4' )
158+ CAMS_flag = True
183159 except :
184- EAC4_flag = False
185- print ('EAC4 data missing. Skipping...' )
160+ CAMS_flag = False
161+ print ('CAMS data missing. Skipping...' )
186162
187- if EAC4_flag :
163+ if CAMS_flag :
188164 try :
189- for EAC4_variable , shortName in EAC4_variables .items ():
190- var = EAC4nc ['reanalysis' ][shortName ]
191- ancillary [EAC4_variable ] = {}
192- ancillary [EAC4_variable ]['value' ] = var .values [0 ][0 ][0 ]
193- ancillary [EAC4_variable ]['units' ] = var .units
194- ancillary [EAC4_variable ]['long_name' ] = var .long_name
195- ancillary [EAC4_variable ]['source' ] = 'EAC4 (ECMWF). https://ads.atmosphere.copernicus.eu/cdsapp#!/dataset/cams-global-atmospheric-composition-forecasts?tab=overview'
165+ for CAMS_variable , shortName in CAMS_variables .items ():
166+ var = CAMSnc ['reanalysis' ][shortName ]
167+ ancillary [CAMS_variable ] = {}
168+ ancillary [CAMS_variable ]['value' ] = var .values [ 0 ] [0 ][0 ][0 ]
169+ ancillary [CAMS_variable ]['units' ] = var .units
170+ ancillary [CAMS_variable ]['long_name' ] = var .long_name
171+ ancillary [CAMS_variable ]['source' ] = 'CAMS (ECMWF). https://ads.atmosphere.copernicus.eu/cdsapp#!/dataset/cams-global-atmospheric-composition-forecasts?tab=overview'
196172 except :
197- print ('Problem processing EAC4 data. Skipping...' )
173+ print ('Problem processing CAMS data. Skipping...' )
198174
199175 return ancillary
200176
@@ -223,11 +199,11 @@ def getAnc_ecmwf(inputGroup):
223199 ancillary = GetAnc_ecmwf .get_ancillary_main (lat [index ], lon [index ], lat_timeStamp , ancPath )
224200
225201 # position retrieval index has been confirmed manually in SeaDAS
226- uWind = ancillary ['10m_u_component_of_wind' ]['value' ][ 0 ]
227- vWind = ancillary ['10m_v_component_of_wind' ]['value' ][ 0 ]
202+ uWind = ancillary ['10m_u_component_of_wind' ]['value' ]
203+ vWind = ancillary ['10m_v_component_of_wind' ]['value' ]
228204 modWind .append (np .sqrt (uWind * uWind + vWind * vWind )) # direction not needed
229205 #ancAOD = aerGroup.getDataset("TOTEXTTAU")
230- modAOD .append (ancillary ['total_aerosol_optical_depth_550nm' ]['value' ][ 0 ] )
206+ modAOD .append (ancillary ['total_aerosol_optical_depth_550nm' ]['value' ])
231207
232208 modData = HDFRoot ()
233209 modGroup = modData .addGroup ('ECMWF' )
0 commit comments