pyVPRM 3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyVPRM/VPRM.py +1118 -0
- pyVPRM/__init__.py +1 -0
- pyVPRM/lib/__init__.py +0 -0
- pyVPRM/lib/downmodis.py +1012 -0
- pyVPRM/lib/fancy_plot.py +75 -0
- pyVPRM/lib/flux_tower_class.py +444 -0
- pyVPRM/lib/fluxnet_info/fluxnet_sites.pkl +0 -0
- pyVPRM/lib/fluxnet_info/site_infos.txt +218 -0
- pyVPRM/lib/functions.py +471 -0
- pyVPRM/meteorologies/__init__.py +0 -0
- pyVPRM/meteorologies/era5_class_dkrz.py +279 -0
- pyVPRM/meteorologies/era5_class_draft.py +60 -0
- pyVPRM/meteorologies/era5_monthly_xr.py +149 -0
- pyVPRM/meteorologies/met_base_class.py +67 -0
- pyVPRM/meteorologies/met_local_measurement.py +56 -0
- pyVPRM/sat_managers/__init__.py +0 -0
- pyVPRM/sat_managers/base_manager.py +430 -0
- pyVPRM/sat_managers/city.py +21 -0
- pyVPRM/sat_managers/copernicus.py +29 -0
- pyVPRM/sat_managers/esa_world_cover.py +21 -0
- pyVPRM/sat_managers/mapbiomas.py +35 -0
- pyVPRM/sat_managers/modis.py +266 -0
- pyVPRM/sat_managers/proba_v.py +86 -0
- pyVPRM/sat_managers/sentinel2.py +192 -0
- pyVPRM/sat_managers/synmap.py +21 -0
- pyVPRM/sat_managers/viirs.py +232 -0
- pyVPRM/sat_managers/viirs09ga.py +173 -0
- pyVPRM/vprm_configs/__init__.py +0 -0
- pyVPRM/vprm_configs/copernicus_land_cover.yaml +94 -0
- pyVPRM/vprm_configs/esa_world_cover.yaml +71 -0
- pyVPRM/vprm_configs/synmap.yaml +106 -0
- pyVPRM/vprm_models/__init__.py +1 -0
- pyVPRM/vprm_models/model_params/__init__.py +0 -0
- pyVPRM/vprm_models/vprm_base.py +691 -0
- pyVPRM/vprm_models/vprm_base_no_xeric.py +585 -0
- pyVPRM/vprm_models/vprm_modified.py +530 -0
- pyVPRM/vprm_models/vprm_nn.py +130 -0
- pyVPRM-3.0.dist-info/LICENSE +21 -0
- pyVPRM-3.0.dist-info/METADATA +40 -0
- pyVPRM-3.0.dist-info/RECORD +42 -0
- pyVPRM-3.0.dist-info/WHEEL +5 -0
- pyVPRM-3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import xarray as xr
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
import numpy as np
|
|
6
|
+
from dateutil import parser
|
|
7
|
+
from scipy.interpolate import interp2d
|
|
8
|
+
import pygrib
|
|
9
|
+
import copy
|
|
10
|
+
import uuid
|
|
11
|
+
import datetime
|
|
12
|
+
from pyVPRM.meteorologies.met_base_class import met_data_handler_base
|
|
13
|
+
from loguru import logger
|
|
14
|
+
|
|
15
|
+
map_function = lambda lon: (lon - 360) if (lon > 180) else lon
|
|
16
|
+
|
|
17
|
+
bpaths = {
|
|
18
|
+
"sf00": "/pool/data/ERA5/E*/sf/an/{}",
|
|
19
|
+
"sf12": "/pool/data/ERA5/E*/sf/fc/{}",
|
|
20
|
+
"pl00": "/pool/data/ERA5/E*/pl/an/{}",
|
|
21
|
+
"ml00": "/pool/data/ERA5/E*/ml/an/{}",
|
|
22
|
+
}
|
|
23
|
+
# Check documentation under
|
|
24
|
+
# https://confluence.ecmwf.int/display/CKB/ERA5%3A+data+documentation#ERA5:datadocumentation-Spatialgrid
|
|
25
|
+
|
|
26
|
+
keys_dict = {
|
|
27
|
+
"ssrd": [169, "sf12"], # surface solar radiation downwards(J/m**2)
|
|
28
|
+
"t2m": [167, "sf00"], # temperature 2 m
|
|
29
|
+
# 'slt': [43, 'sf00'], #soil type, not sure it's useful...MODIS should do better
|
|
30
|
+
"sp": [134, "sf00"], # surface pressure
|
|
31
|
+
"tcc": [164, "sf00"], # total cloud cover
|
|
32
|
+
"stl1": [139, "sf00"], # soil temperature level1
|
|
33
|
+
"stl2": [170, "sf00"], # soil temperature level2
|
|
34
|
+
"stl3": [183, "sf00"], # soil temperature level3
|
|
35
|
+
"swvl1": [39, "sf00"], # soil water level 1
|
|
36
|
+
"swvl2": [40, "sf00"], # soil water level 2
|
|
37
|
+
"swvl3": [41, "sf00"], # soil water level 3
|
|
38
|
+
"swvl4": [42, "sf00"], # soil water level 3
|
|
39
|
+
"2d": [168, "sf00"], # 2-m dewpoint temperature
|
|
40
|
+
"tp": [228, "sf12"], # total precipitation over given time
|
|
41
|
+
# 'ssr': [176, 'sf12'], #net surface solar radiation (J/m**2)
|
|
42
|
+
# 'str': [177, 'sf12'],#net surface thermal radiation (J/m**2)
|
|
43
|
+
"src": [198, "sf00"],
|
|
44
|
+
"q": [133, "ml00"], # specific humidity (%)
|
|
45
|
+
"e": [182, "sf12"],
|
|
46
|
+
"sst": [34, "sf00"],
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class met_data_handler(met_data_handler_base):
|
|
51
|
+
"""
|
|
52
|
+
Class for using ERA5 data available on Levante's DKRZ cluster.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, year, month, day=None, hour=None, keys=[], timesteps="hourly"):
|
|
56
|
+
super().__init__()
|
|
57
|
+
if timesteps == "hourly":
|
|
58
|
+
self.add_time_str = "1H"
|
|
59
|
+
elif timesteps == "daily":
|
|
60
|
+
self.add_time_str = "1D"
|
|
61
|
+
elif timesteps == "monthly":
|
|
62
|
+
self.add_time_str = "1M"
|
|
63
|
+
self.file_handlers = dict()
|
|
64
|
+
self.in_era5_grid = True
|
|
65
|
+
self.regridder = None
|
|
66
|
+
self.ds_in_t = None
|
|
67
|
+
self.reg_lats = []
|
|
68
|
+
self.reg_lons = []
|
|
69
|
+
if len(keys) == 0:
|
|
70
|
+
self.keys = keys_dict.keys()
|
|
71
|
+
else:
|
|
72
|
+
self.keys = keys
|
|
73
|
+
self.change_date(year, month, day, hour)
|
|
74
|
+
|
|
75
|
+
def _init_data_for_day(self):
|
|
76
|
+
file_dict = dict()
|
|
77
|
+
for c, key in enumerate(self.keys):
|
|
78
|
+
t_dict = dict()
|
|
79
|
+
bpath = bpaths[keys_dict[key][1]].format(self.add_time_str)
|
|
80
|
+
e_id = keys_dict[key][0]
|
|
81
|
+
if self.add_time_str == "1H":
|
|
82
|
+
fname = os.path.join(
|
|
83
|
+
bpath,
|
|
84
|
+
"{:03d}/E*{}_1H_{}-{:02d}-{:02d}_{:03d}.grb".format(
|
|
85
|
+
e_id, keys_dict[key][1], self.year, self.month, self.day, e_id
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
elif self.add_time_str == "1D":
|
|
89
|
+
fname = os.path.join(
|
|
90
|
+
bpath,
|
|
91
|
+
"{:03d}/E*{}_1D_{}-{:02d}_{:03d}.grb".format(
|
|
92
|
+
e_id, keys_dict[key][1], self.year, self.month, e_id
|
|
93
|
+
),
|
|
94
|
+
)
|
|
95
|
+
elif self.add_time_str == "1M":
|
|
96
|
+
fname = os.path.join(
|
|
97
|
+
bpath,
|
|
98
|
+
"{:03d}/E*{}_1M_{}_{:03d}.grb".format(
|
|
99
|
+
e_id, keys_dict[key][1], self.year, e_id
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
fname = glob.glob(fname)
|
|
103
|
+
fname = [i for i in fname if "EL" not in i]
|
|
104
|
+
if key in self.file_handlers.keys():
|
|
105
|
+
self.file_handlers[key]["current"].close()
|
|
106
|
+
t_dict["current"] = pygrib.open(fname[0])
|
|
107
|
+
t_dict["current_name"] = list(fname)
|
|
108
|
+
t_dict["current_ind"] = 0
|
|
109
|
+
t_dict["type"] = keys_dict[key][1]
|
|
110
|
+
self.file_handlers[key] = t_dict
|
|
111
|
+
if not "sf" in keys_dict[key][1]: # Not a surface value
|
|
112
|
+
self.select_from_pressure_levels(key)
|
|
113
|
+
if self.ds_in_t is None:
|
|
114
|
+
lats, lons = t_dict["current"][1].latlons()
|
|
115
|
+
self.ds_in_t = xr.Dataset(
|
|
116
|
+
{
|
|
117
|
+
"lat": (["lat"], lats[:, 0], {"units": "degrees_north"}),
|
|
118
|
+
"lon": (
|
|
119
|
+
["lon"],
|
|
120
|
+
[map_function(i) for i in lons[0]],
|
|
121
|
+
{"units": "degrees_east"},
|
|
122
|
+
),
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
self.ds_in_t = self.ds_in_t.set_coords(["lon", "lat"])
|
|
126
|
+
|
|
127
|
+
def _load_data_for_hour(self):
|
|
128
|
+
# Caution: The date as argument corresponds to the END of the ERA5 integration time.
|
|
129
|
+
data_dict = dict()
|
|
130
|
+
if self.add_time_str == "1H":
|
|
131
|
+
for key in self.keys:
|
|
132
|
+
data_dict[key] = (
|
|
133
|
+
["lat", "lon"],
|
|
134
|
+
self.file_handlers[key]["current"][int(self.hour + 1)].values,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
elif self.add_time_str == "1D":
|
|
138
|
+
for key in self.keys:
|
|
139
|
+
data_dict[key] = (
|
|
140
|
+
["lat", "lon"],
|
|
141
|
+
self.file_handlers[key]["current"][int(self.day)].values,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
elif self.add_time_str == "1M":
|
|
145
|
+
for key in self.keys:
|
|
146
|
+
if self.month not in [
|
|
147
|
+
self.file_handlers[key]["current"][t + 1].month
|
|
148
|
+
for t in range(self.file_handlers[key]["current"].messages)
|
|
149
|
+
]:
|
|
150
|
+
for i in self.file_handlers[key]["current_name"]:
|
|
151
|
+
tmp = pygrib.open(i)
|
|
152
|
+
if self.month in [
|
|
153
|
+
tmp[t + 1].month for t in range(tmp.messages)
|
|
154
|
+
]:
|
|
155
|
+
self.file_handlers[key]["current"] = tmp
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
if self.month not in [
|
|
159
|
+
self.file_handlers[key]["current"][t + 1].month
|
|
160
|
+
for t in range(self.file_handlers[key]["current"].messages)
|
|
161
|
+
]:
|
|
162
|
+
logger.info("No data for given month")
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
for c in range(self.file_handlers[key]["current"].messages):
|
|
166
|
+
if self.file_handlers[key]["current"][c + 1].month == self.month:
|
|
167
|
+
break
|
|
168
|
+
data_dict[key] = (
|
|
169
|
+
["lat", "lon"],
|
|
170
|
+
self.file_handlers[key]["current"][c + 1].values,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
self.ds_out = copy.deepcopy(self.ds_in_t)
|
|
174
|
+
self.ds_out = self.ds_out.assign(data_dict)
|
|
175
|
+
self.ds_out = self.ds_out.sortby("lon")
|
|
176
|
+
self.in_era5_grid = True
|
|
177
|
+
|
|
178
|
+
def get_all_interpolators(self, day, hour):
|
|
179
|
+
ret_dict = dict()
|
|
180
|
+
for key in self.keys:
|
|
181
|
+
ret_dict[key] = self.get_interpolator()
|
|
182
|
+
return ret_dict
|
|
183
|
+
|
|
184
|
+
def regrid(
|
|
185
|
+
self,
|
|
186
|
+
lats=None,
|
|
187
|
+
lons=None,
|
|
188
|
+
dataset=None,
|
|
189
|
+
n_cpus=1,
|
|
190
|
+
weights=None,
|
|
191
|
+
overwrite_regridder=False,
|
|
192
|
+
):
|
|
193
|
+
|
|
194
|
+
import xesmf as xe
|
|
195
|
+
|
|
196
|
+
if self.in_era5_grid is False:
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
if (self.regridder is None) | (overwrite_regridder):
|
|
200
|
+
if (lats is not None) and (lons is not None):
|
|
201
|
+
t_ds_out = xr.Dataset(
|
|
202
|
+
{
|
|
203
|
+
"lat": (["lat"], lats, {"units": "degrees_north"}),
|
|
204
|
+
"lon": (["lon"], lons, {"units": "degrees_east"}),
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
t_ds_out = t_ds_out.set_coords(["lon", "lat"])
|
|
208
|
+
self.reg_lats = lats
|
|
209
|
+
self.reg_lons = lons
|
|
210
|
+
else:
|
|
211
|
+
t_ds_out = dataset
|
|
212
|
+
|
|
213
|
+
if (weights is not None) & os.path.exists(str(weights)):
|
|
214
|
+
logger.info("Load weights from {}".format(weights))
|
|
215
|
+
else:
|
|
216
|
+
bfolder = os.path.dirname(weights)
|
|
217
|
+
src_temp_path = os.path.join(bfolder, "{}.nc".format(str(uuid.uuid4())))
|
|
218
|
+
dest_temp_path = os.path.join(
|
|
219
|
+
bfolder, "{}.nc".format(str(uuid.uuid4()))
|
|
220
|
+
)
|
|
221
|
+
self.ds_out.to_netcdf(src_temp_path)
|
|
222
|
+
t_ds_out.to_netcdf(dest_temp_path)
|
|
223
|
+
cmd = "mpirun -np {} ESMF_RegridWeightGen --source {} --destination {} --weight {} -m bilinear --64bit_offset --extrap_method nearestd --no_log".format(
|
|
224
|
+
n_cpus, src_temp_path, dest_temp_path, weights
|
|
225
|
+
)
|
|
226
|
+
logger.info(cmd)
|
|
227
|
+
os.system(cmd)
|
|
228
|
+
os.remove(src_temp_path)
|
|
229
|
+
os.remove(dest_temp_path)
|
|
230
|
+
|
|
231
|
+
self.regridder = xe.Regridder(
|
|
232
|
+
self.ds_in_t, t_ds_out, "bilinear", weights=weights, reuse_weights=True
|
|
233
|
+
)
|
|
234
|
+
self.ds_out = self.regridder(self.ds_out)
|
|
235
|
+
self.in_era5_grid = False
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
def select_from_pressure_levels(self, key):
|
|
239
|
+
selection_args = {}
|
|
240
|
+
if key == "r":
|
|
241
|
+
selection_args["level"] = 975
|
|
242
|
+
selection_args["typeOfLevel"] = "isobaricInhPa"
|
|
243
|
+
elif key == "q":
|
|
244
|
+
selection_args["level"] = 137
|
|
245
|
+
selection_args["typeOfLevel"] = "hybrid"
|
|
246
|
+
try:
|
|
247
|
+
self.file_handlers[key]["current"] = self.file_handlers[key][
|
|
248
|
+
"current"
|
|
249
|
+
].select(**selection_args)
|
|
250
|
+
except Exception as e:
|
|
251
|
+
logger.info("No era5 data found for {}".format(selection_args))
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
def get_data(self, lonlat=None, key=None):
|
|
255
|
+
if key is not None:
|
|
256
|
+
tmp = self.ds_out[key]
|
|
257
|
+
else:
|
|
258
|
+
tmp = self.ds_out
|
|
259
|
+
if lonlat is None:
|
|
260
|
+
return tmp
|
|
261
|
+
else:
|
|
262
|
+
lon = lonlat[0]
|
|
263
|
+
if isinstance(lon, list) | isinstance(lon, np.ndarray):
|
|
264
|
+
return tmp.interp(lon=("z", lon), lat=("z", lonlat[1]), method="linear")
|
|
265
|
+
else:
|
|
266
|
+
lon = lonlat[0]
|
|
267
|
+
return tmp.interp(lon=lon, lat=lonlat[1])
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
if __name__ == "__main__":
|
|
271
|
+
year = "2000"
|
|
272
|
+
month = 2
|
|
273
|
+
day = 20
|
|
274
|
+
hour = 5 # UTC hour
|
|
275
|
+
position = {"lat": 50.30493, "long": 5.99812}
|
|
276
|
+
era5_handler = met_data_handler(year, month, day)
|
|
277
|
+
era5_handler.change_date(hour)
|
|
278
|
+
ret = era5_handler.get_data()
|
|
279
|
+
logger.info(ret)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import xarray as xr
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
import numpy as np
|
|
6
|
+
from dateutil import parser
|
|
7
|
+
from scipy.interpolate import interp2d
|
|
8
|
+
import pygrib
|
|
9
|
+
import copy
|
|
10
|
+
import xesmf as xe
|
|
11
|
+
import uuid
|
|
12
|
+
import datetime
|
|
13
|
+
from pyVPRM.meteorologies.met_base_class import met_data_handler_base
|
|
14
|
+
from loguru import logger
|
|
15
|
+
|
|
16
|
+
class met_data_handler(met_data_handler_base):
|
|
17
|
+
|
|
18
|
+
def __init__(self, year, month, day, hour, keys=[]):
|
|
19
|
+
super().__init__()
|
|
20
|
+
|
|
21
|
+
# Init with year, month, day, hour and the required era5 keys as given in the
|
|
22
|
+
# keys_dict above
|
|
23
|
+
|
|
24
|
+
def regrid(
|
|
25
|
+
self,
|
|
26
|
+
lats=None,
|
|
27
|
+
lons=None,
|
|
28
|
+
dataset=None,
|
|
29
|
+
n_cpus=1,
|
|
30
|
+
weights=None,
|
|
31
|
+
overwrite_regridder=False,
|
|
32
|
+
):
|
|
33
|
+
# Regrid the ERA5 data to given grid with lats and lons or an xarray
|
|
34
|
+
# with given coords alternatively. If regridding weights are given to not recalculate
|
|
35
|
+
# them.
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
def get_data(self, lonlat=None, key=None):
|
|
39
|
+
# Return ERA5 data for lonlat if lonlat is not None else return all data.
|
|
40
|
+
# Pick a specific key if key is not None. Return as xarray dataset
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
def _init_data_for_day(self):
|
|
44
|
+
# If something should be done if a new date is provided
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
def _load_data_for_hour(self):
|
|
48
|
+
# If something should be done if only the hour is changed
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
year = "2000"
|
|
54
|
+
month = 2
|
|
55
|
+
day = 20
|
|
56
|
+
hour = 5 # UTC hour
|
|
57
|
+
era5_handler = class_name(year, month, day)
|
|
58
|
+
era5_handler.change_date(hour)
|
|
59
|
+
ret = era5_handler.get_data()
|
|
60
|
+
logger.info(ret)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import xarray as xr
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
import numpy as np
|
|
6
|
+
from dateutil import parser
|
|
7
|
+
from scipy.interpolate import interp2d
|
|
8
|
+
import pygrib
|
|
9
|
+
import copy
|
|
10
|
+
import xesmf as xe
|
|
11
|
+
import uuid
|
|
12
|
+
import datetime
|
|
13
|
+
from pyVPRM.meteorologies.met_base_class import met_data_handler_base
|
|
14
|
+
from loguru import logger
|
|
15
|
+
|
|
16
|
+
class met_data_handler(met_data_handler_base):
|
|
17
|
+
|
|
18
|
+
def __init__(self, year, month, day, hour, bpath, mpi=False, keys=[]):
|
|
19
|
+
super().__init__()
|
|
20
|
+
# Init with year, month, day, hour and the required era5 keys as given in the
|
|
21
|
+
# keys_dict above
|
|
22
|
+
self.in_era5_grid = True
|
|
23
|
+
self.this_month = 0
|
|
24
|
+
self.this_year = 0
|
|
25
|
+
self.bpath = bpath
|
|
26
|
+
self.ds_in_t = None
|
|
27
|
+
self.regridder = None
|
|
28
|
+
self.mpi = mpi
|
|
29
|
+
self.change_date(year, month, day, hour)
|
|
30
|
+
|
|
31
|
+
def regrid(
|
|
32
|
+
self,
|
|
33
|
+
lats=None,
|
|
34
|
+
lons=None,
|
|
35
|
+
dataset=None,
|
|
36
|
+
n_cpus=1,
|
|
37
|
+
weights=None,
|
|
38
|
+
overwrite_regridder=False,
|
|
39
|
+
):
|
|
40
|
+
|
|
41
|
+
import xesmf as xe
|
|
42
|
+
|
|
43
|
+
if self.in_era5_grid is False:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
if (self.regridder is None) | (overwrite_regridder):
|
|
47
|
+
if (lats is not None) and (lons is not None):
|
|
48
|
+
t_ds_out = xr.Dataset(
|
|
49
|
+
{
|
|
50
|
+
"lat": (["lat"], lats, {"units": "degrees_north"}),
|
|
51
|
+
"lon": (["lon"], lons, {"units": "degrees_east"}),
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
t_ds_out = t_ds_out.set_coords(["lon", "lat"])
|
|
55
|
+
self.reg_lats = lats
|
|
56
|
+
self.reg_lons = lons
|
|
57
|
+
else:
|
|
58
|
+
t_ds_out = dataset
|
|
59
|
+
|
|
60
|
+
if (weights is not None) & os.path.exists(str(weights)):
|
|
61
|
+
logger.info("Load weights from {}".format(weights))
|
|
62
|
+
else:
|
|
63
|
+
bfolder = os.path.dirname(weights)
|
|
64
|
+
src_temp_path = os.path.join(bfolder, "{}.nc".format(str(uuid.uuid4())))
|
|
65
|
+
dest_temp_path = os.path.join(
|
|
66
|
+
bfolder, "{}.nc".format(str(uuid.uuid4()))
|
|
67
|
+
)
|
|
68
|
+
self.ds_in_t.to_netcdf(src_temp_path)
|
|
69
|
+
t_ds_out.to_netcdf(dest_temp_path)
|
|
70
|
+
cmd = "ESMF_RegridWeightGen --source {} --destination {} --weight {} -m bilinear --64bit_offset --extrap_method nearestd --no_log".format(
|
|
71
|
+
src_temp_path, dest_temp_path, weights
|
|
72
|
+
)
|
|
73
|
+
if self.mpi:
|
|
74
|
+
cmd = "mpirun -np {} ".format(n_cpus) + cmd
|
|
75
|
+
logger.info(cmd)
|
|
76
|
+
os.system(cmd)
|
|
77
|
+
os.remove(src_temp_path)
|
|
78
|
+
os.remove(dest_temp_path)
|
|
79
|
+
|
|
80
|
+
self.regridder = xe.Regridder(
|
|
81
|
+
self.data, t_ds_out, "bilinear", weights=weights, reuse_weights=True
|
|
82
|
+
)
|
|
83
|
+
self.data = self.regridder(self.data)
|
|
84
|
+
self.in_era5_grid = False
|
|
85
|
+
|
|
86
|
+
def get_data(self, lonlat=None, key=None):
|
|
87
|
+
# Return ERA5 data for lonlat if lonlat is not None else return all data.
|
|
88
|
+
# Pick a specific key if key is not None. Return as xarray dataset
|
|
89
|
+
dt = np.datetime64(
|
|
90
|
+
"{}-{:02d}-{:02d}T{:02d}:00:00.000000".format(
|
|
91
|
+
self.year, self.month, self.day, self.hour
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
if key is not None:
|
|
95
|
+
tmp = self.data.sel({"time": dt})[key]
|
|
96
|
+
else:
|
|
97
|
+
tmp = self.data.sel({"time": dt})
|
|
98
|
+
if lonlat is None:
|
|
99
|
+
return tmp
|
|
100
|
+
else:
|
|
101
|
+
lon = lonlat[0]
|
|
102
|
+
if isinstance(lon, list) | isinstance(lon, np.ndarray):
|
|
103
|
+
return tmp.interp(lon=("z", lon), lat=("z", lonlat[1]), method="linear")
|
|
104
|
+
else:
|
|
105
|
+
lon = lonlat[0]
|
|
106
|
+
return tmp.interp(lon=lon, lat=lonlat[1])
|
|
107
|
+
return self.data
|
|
108
|
+
|
|
109
|
+
def _init_data_for_day(self):
|
|
110
|
+
if (self.this_month != self.month) | (self.this_year != self.year):
|
|
111
|
+
self.data = xr.open_dataset(
|
|
112
|
+
os.path.join(self.bpath, "{}_{}.nc".format(self.year, self.month))
|
|
113
|
+
)
|
|
114
|
+
self.this_month = self.month
|
|
115
|
+
self.this_year = self.year
|
|
116
|
+
self.in_era5_grid = True
|
|
117
|
+
|
|
118
|
+
if self.ds_in_t is None:
|
|
119
|
+
self.ds_in_t = xr.Dataset(
|
|
120
|
+
{
|
|
121
|
+
"lat": (
|
|
122
|
+
["lat"],
|
|
123
|
+
self.data["lat"].values,
|
|
124
|
+
{"units": "degrees_north"},
|
|
125
|
+
),
|
|
126
|
+
"lon": (
|
|
127
|
+
["lon"],
|
|
128
|
+
self.data["lon"].values,
|
|
129
|
+
{"units": "degrees_east"},
|
|
130
|
+
),
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
self.ds_in_t = self.ds_in_t.set_coords(["lon", "lat"])
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
def _load_data_for_hour(self):
|
|
137
|
+
# If something should be done if only the hour is changed
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if __name__ == "__main__":
|
|
142
|
+
year = "2000"
|
|
143
|
+
month = 2
|
|
144
|
+
day = 20
|
|
145
|
+
hour = 5 # UTC hour
|
|
146
|
+
era5_handler = class_name(year, month, day)
|
|
147
|
+
era5_handler.change_date(hour)
|
|
148
|
+
ret = era5_handler.get_data()
|
|
149
|
+
logger.info(ret)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import xarray as xr
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
import numpy as np
|
|
6
|
+
from dateutil import parser
|
|
7
|
+
import pygrib
|
|
8
|
+
import copy
|
|
9
|
+
import uuid
|
|
10
|
+
import datetime
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class met_data_handler_base:
|
|
14
|
+
"""
|
|
15
|
+
Base class for all meteorologies
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self.year = None
|
|
20
|
+
self.month = None
|
|
21
|
+
self.day = None
|
|
22
|
+
self.hour = None
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
def change_date(self, year=None, month=None, day=None, hour=None):
|
|
26
|
+
# Caution: The date as argument corresponds to the END of the ERA5 integration time.
|
|
27
|
+
|
|
28
|
+
new_date = False
|
|
29
|
+
if (day != self.day) & (day != None):
|
|
30
|
+
self.day = day
|
|
31
|
+
new_date = True
|
|
32
|
+
|
|
33
|
+
if (month != self.month) & (month != None):
|
|
34
|
+
self.month = month
|
|
35
|
+
new_date = True
|
|
36
|
+
|
|
37
|
+
if (year != self.year) & (year != None):
|
|
38
|
+
self.year = year
|
|
39
|
+
new_date = True
|
|
40
|
+
|
|
41
|
+
if new_date:
|
|
42
|
+
self._init_data_for_day()
|
|
43
|
+
|
|
44
|
+
if (hour != self.hour) & (hour != None):
|
|
45
|
+
self.hour = hour
|
|
46
|
+
new_date = True
|
|
47
|
+
|
|
48
|
+
if new_date:
|
|
49
|
+
self._load_data_for_hour()
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
def _init_data_for_day(self):
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
def _load_data_for_hour(self):
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
def regrid(
|
|
59
|
+
self,
|
|
60
|
+
lats=None,
|
|
61
|
+
lons=None,
|
|
62
|
+
dataset=None,
|
|
63
|
+
n_cpus=1,
|
|
64
|
+
weights=None,
|
|
65
|
+
overwrite_regridder=False,
|
|
66
|
+
):
|
|
67
|
+
return
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import xarray as xr
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
import numpy as np
|
|
6
|
+
from dateutil import parser
|
|
7
|
+
import copy
|
|
8
|
+
import datetime
|
|
9
|
+
from pyVPRM.meteorologies.met_base_class import met_data_handler_base
|
|
10
|
+
import pandas as pd
|
|
11
|
+
from loguru import logger
|
|
12
|
+
|
|
13
|
+
key_map = {"time": "TIMESTAMP_START", "ssrd": "SW_IN", "t2m": "TA"}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class met_data_handler(met_data_handler_base):
|
|
17
|
+
|
|
18
|
+
def __init__(self, data_file, names=key_map):
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.names = names
|
|
21
|
+
self.non_time_keys = [
|
|
22
|
+
self.names[i] for i in self.names.keys() if i not in "time"
|
|
23
|
+
]
|
|
24
|
+
self.data = pd.read_csv(
|
|
25
|
+
data_file, usecols=[self.names[i] for i in self.names.keys()]
|
|
26
|
+
)
|
|
27
|
+
self.data[self.names["time"]] = pd.to_datetime(self.data[self.names["time"]])
|
|
28
|
+
self.data[self.names["t2m"]] = self.data[self.names["t2m"]] + 273.15
|
|
29
|
+
self.data[self.names["ssrd"]] = self.data[self.names["ssrd"]] * 3600
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
def get_data(self, lonlat=None, key=None):
|
|
33
|
+
# Return ERA5 data for lonlat if lonlat is not None else return all data.
|
|
34
|
+
# Pick a specific key if key is not None. Return as xarray dataset
|
|
35
|
+
|
|
36
|
+
date_int = np.datetime64(
|
|
37
|
+
"{}-{:02d}-{:02d} {:02d}:00:00".format(
|
|
38
|
+
self.year, self.month, self.day, self.hour
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
row = self.data.loc[self.data[self.names["time"]] == date_int]
|
|
42
|
+
if key is None:
|
|
43
|
+
return row[self.non_time_keys].values
|
|
44
|
+
else:
|
|
45
|
+
return float(row[self.names[key]].values)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == "__main__":
|
|
49
|
+
year = "2000"
|
|
50
|
+
month = 2
|
|
51
|
+
day = 20
|
|
52
|
+
hour = 5 # UTC hour
|
|
53
|
+
era5_handler = ERA5(year, month, day)
|
|
54
|
+
era5_handler.change_date(hour)
|
|
55
|
+
ret = era5_handler.get_data()
|
|
56
|
+
logger.info(ret)
|
|
File without changes
|