This notebook will provide you guidance how to explore and plot ECMWF open dataset to produce the map from the ECMWF open charts web product.
The original product can be found on this link: https://charts.ecmwf.int/products/medium-10ws-mean-spread
The full list of available Open data products can be found here, and more information can be found in the User documentation.
Access to ECMWF Open data is governed by the Creative Commons CC-BY-4.0 licence and associated Terms of Use.
In applying this licence, ECMWF does not waive the privileges and immunities granted to it by virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction
To find out how to obtain the access to the full forecast dataset at higher resolution please visit our Access page.
This product takes in input 2 parameters :
In this example, we will use:
First we need to install them in the current Jupyter kernel:
#!pip install ecmwf-opendata metview metview-python
import metview as mv
from ecmwf.opendata import Client
client = Client("ecmwf", beta=True)
parameters = ['10u','10v']
filename = 'medium-10ws-mean-spread.grib'
filename
'medium-10ws-mean-spread.grib'
To calculate the mean and spread of the 10m wind speed, we need to retrieve all the ensemble members at the given time.
We need to put both cf and pf as type type to download all 50 ensemble members as well as the control forecast.
Setting date to 0 will download today's data.
Removing date and time altogether from the request will download the latest data.
Try commenting out date and time to download latest forecast!
client.retrieve(
date=0,
time=0,
step=72,
stream="enfo",
type=['cf', 'pf'],
levtype="sfc",
param=parameters,
target=filename
)
20241223000000-72h-enfo-ef.grib2: 0%| | 0.00/83.8M [00:00<?, ?B/s]
<ecmwf.opendata.client.Result at 0x1130bac50>
Now we can use ecmwf.data to read the file.
data = mv.read(filename)
The describe() function will give us the overview of the dataset.
data.describe()
parameter | typeOfLevel | level | date | time | step | number | paramId | class | stream | type | experimentVersionNumber |
---|---|---|---|---|---|---|---|---|---|---|---|
10u | heightAboveGround | 10 | 20241223 | 0 | 72 | 0,1,... | 165 | od | enfo | cf,pf | 0001 |
10v | heightAboveGround | 10 | 20241223 | 0 | 72 | 0,1,... | 166 | od | enfo | cf,pf | 0001 |
We can use ls() function to list all the fields in the file we downloaded.
Note that this fieldset has all fields randomly arranged. You can see that if you look at the number column. These are ensemble member numbers.
data.ls()
centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | number | gridType | |
---|---|---|---|---|---|---|---|---|---|---|
Message | ||||||||||
0 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 24 | regular_ll |
1 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 14 | regular_ll |
2 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 41 | regular_ll |
3 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 42 | regular_ll |
4 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 50 | regular_ll |
5 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 23 | regular_ll |
6 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 15 | regular_ll |
7 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 6 | regular_ll |
8 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 33 | regular_ll |
9 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 32 | regular_ll |
10 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 24 | regular_ll |
11 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 42 | regular_ll |
12 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 33 | regular_ll |
13 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 15 | regular_ll |
14 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 34 | regular_ll |
15 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 25 | regular_ll |
16 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 6 | regular_ll |
17 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 7 | regular_ll |
18 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 43 | regular_ll |
19 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 17 | regular_ll |
20 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 35 | regular_ll |
21 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 26 | regular_ll |
22 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 44 | regular_ll |
23 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 16 | regular_ll |
24 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 34 | regular_ll |
25 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 25 | regular_ll |
26 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 8 | regular_ll |
27 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 7 | regular_ll |
28 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 43 | regular_ll |
29 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 27 | regular_ll |
30 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 35 | regular_ll |
31 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 16 | regular_ll |
32 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 9 | regular_ll |
33 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 17 | regular_ll |
34 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 44 | regular_ll |
35 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 26 | regular_ll |
36 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 18 | regular_ll |
37 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 45 | regular_ll |
38 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 36 | regular_ll |
39 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 8 | regular_ll |
40 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 9 | regular_ll |
41 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 27 | regular_ll |
42 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 45 | regular_ll |
43 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | cf | 0 | regular_ll |
44 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 36 | regular_ll |
45 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 37 | regular_ll |
46 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 1 | regular_ll |
47 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 18 | regular_ll |
48 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 28 | regular_ll |
49 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 19 | regular_ll |
50 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 46 | regular_ll |
51 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 37 | regular_ll |
52 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | cf | 0 | regular_ll |
53 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 1 | regular_ll |
54 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 28 | regular_ll |
55 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 38 | regular_ll |
56 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 10 | regular_ll |
57 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 29 | regular_ll |
58 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 46 | regular_ll |
59 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 2 | regular_ll |
60 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 19 | regular_ll |
61 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 47 | regular_ll |
62 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 3 | regular_ll |
63 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 29 | regular_ll |
64 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 38 | regular_ll |
65 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 10 | regular_ll |
66 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 20 | regular_ll |
67 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 48 | regular_ll |
68 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 47 | regular_ll |
69 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 39 | regular_ll |
70 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 21 | regular_ll |
71 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 11 | regular_ll |
72 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 2 | regular_ll |
73 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 30 | regular_ll |
74 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 3 | regular_ll |
75 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 49 | regular_ll |
76 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 12 | regular_ll |
77 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 4 | regular_ll |
78 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 20 | regular_ll |
79 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 21 | regular_ll |
80 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 48 | regular_ll |
81 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 39 | regular_ll |
82 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 11 | regular_ll |
83 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 5 | regular_ll |
84 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 13 | regular_ll |
85 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 40 | regular_ll |
86 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 31 | regular_ll |
87 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 12 | regular_ll |
88 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 4 | regular_ll |
89 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 30 | regular_ll |
90 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 22 | regular_ll |
91 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 49 | regular_ll |
92 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 23 | regular_ll |
93 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 14 | regular_ll |
94 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 41 | regular_ll |
95 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 50 | regular_ll |
96 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 22 | regular_ll |
97 | ecmf | 10u | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 32 | regular_ll |
98 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 31 | regular_ll |
99 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 5 | regular_ll |
100 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 13 | regular_ll |
101 | ecmf | 10v | heightAboveGround | 10 | 20241223 | 0 | 72 | pf | 40 | regular_ll |
ecmwf.data library has a built in function to calculate the speed from u and v components. It takes u/v fieldsets as input and gives back speed intensity.
Note that we can give a fieldset on N u/v components, not just one pair.
In this case we will calculate all 51 ensemble members at the same time.
First we need to filter out u and v and sort the fieldsets.
u = data.select(shortName= '10u').sort()
v = data.select(shortName= '10v').sort()
And then we calculate the wind speed. Metview has builti in function for this.
speed = mv.speed(u,v)
speed.describe()
parameter | typeOfLevel | level | date | time | step | number | paramId | class | stream | type | experimentVersionNumber |
---|---|---|---|---|---|---|---|---|---|---|---|
10si | heightAboveGround | 10 | 20241223 | 0 | 72 | 0,1,... | 207 | od | enfo | cf,pf | 0001 |
In the end we still need to calculate the mean and spread (as standard deviation) of the wind speed.
ecmwf.data has built in functions to calculate mean and standard deviation of a given fieldset.
Since our data has only one date,time and step, mean() and stdev() will be calculated over the 51 ensemble members.
ws_mean = mv.mean(speed)
ws_spread = mv.stdev(speed)
And finally, we can plot the data on the map.
# define coastlines
coast = mv.mcoast(
map_coastline_colour="charcoal",
map_coastline_resolution="medium",
map_coastline_land_shade="on",
map_coastline_land_shade_colour="cream",
map_coastline_sea_shade="off",
map_boundaries="on",
map_boundaries_colour= "charcoal",
map_boundaries_thickness = 1,
map_disputed_boundaries = "off",
map_grid_colour="tan",
map_label_height=0.35,
)
# define view
view = mv.geoview(
area_mode="name",
area_name="europe",
coastlines=coast
)
#define styles
speed_mean_contour = mv.mcont(legend = "on",
contour_highlight_colour = 'black',
contour_highlight_thickness = 4,
contour_interval = 5,
contour_label = 'on',
contour_label_frequency = 1,
contour_label_height = 0.4,
contour_level_selection_type = 'interval',
contour_line_colour = 'black',
contour_line_thickness = 2)
speed_spread_shade = mv.mcont(legend= "on",
contour_automatics_settings = "style_name",
contour_style_name = "sh_blu_f02t30")
title = mv.mtext(
text_lines=["Ensemble mean and spread for 10m wind speed",
"START TIME: <grib_info key='base-date' format='%a %d %B %Y %H' />",
"VALID TIME: <grib_info key='valid-date' format='%a %d %B %Y %H' />, STEP: <grib_info key='step' />"],
text_font_size=0.4,
text_colour = 'charcoal')
ecmwf_text = mv.mtext(
text_lines = ["© European Centre for Medium-Range Weather Forecasts (ECMWF)",
"Source: www.ecmwf.int Licence: CC-BY-4.0 and ECMWF Terms of Use",
"https://apps.ecmwf.int/datasets/licences/general/"],
text_justification = 'center',
text_font_size = 0.3,
text_mode = "positional",
text_box_x_position = 6.,
text_box_y_position = -0.2,
text_box_x_length = 8,
text_box_y_length = 2,
text_colour = 'charcoal')
# generate plot
mv.setoutput('jupyter', plot_widget=False)
mv.plot(view, ws_spread, speed_spread_shade, ws_mean, speed_mean_contour, title, ecmwf_text)
To generate the png file you can run the following cell.
png = mv.png_output(
output_name = "medium-10ws-mean-spread", # specify relative or full path
output_title = "medium-10ws-mean-spread", # title used by a viewer
output_width = 1000, # set width in pixels
)
mv.setoutput(png)
mv.plot(view, ws_spread, speed_spread_shade, ws_mean, speed_mean_contour, title, ecmwf_text)
Note that plot produced using open data dataset will slightly differ from one from Open Charts. This is due to different resolution of the data.
Open data is on 0.25x0.25 resolution, while high resolution data is 0.1x0.1 grid.