Students: Linhard Rosina, Zappini Carolin
# Import the tools we are going to need today:
import matplotlib.pyplot as plt # plotting library
import numpy as np # numerical library
import xarray as xr # netCDF library
import pandas as pd # tabular library
import cartopy # Map projections libary
import cartopy.crs as ccrs # Projections list
# Some defaults:
plt.rcParams['figure.figsize'] = (12, 5) # Default plot size
Open the ERA5 temperature data:
ds = xr.open_dataset('ERA5_LowRes_Monthly_t2m.nc')
#temporal mean temperature
t_avg_K = ds.t2m.mean(dim='time')
t_avg_C = t_avg_K - 273.15
ax = plt.axes(projection=ccrs.Robinson())
t_avg_C.plot(ax=ax, transform=ccrs.PlateCarree(), cbar_kwargs={'label': '°C'})
ax.coastlines(); ax.gridlines()
ax.set_title('Temporal mean temperature');
# zonal anomaly map of average temperature
t_avg_anom = t_avg_C - t_avg_C.mean(dim='longitude')
ax = plt.axes(projection=ccrs.Robinson())
t_avg_anom.plot(ax=ax, transform=ccrs.PlateCarree(), cbar_kwargs={'label': '°C'})
ax.coastlines(); ax.gridlines()
ax.set_title('Zonal anomaly map of average temperature');
.groupby()
command we learned in the lesson. Now plot the average monthly temperature range, i.e. ¯TMmax - ¯TMmin on a map.#monthly average temperature
tm = ds.t2m.groupby('time.month').mean() - 273.15
latitude = ds['latitude'].values
longitude = ds['longitude'].values
# Plotting the monthly average temperature as a heatmap
plt.figure(figsize=(10, 6))
plt.pcolormesh(longitude, latitude, tm.values[0, :, :], cmap='viridis')
plt.title('Monthly Average 2m Temperature')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.colorbar(label='Temperature (°C)')
plt.show();
# average monthly temperature range
trange = ds.t2m.groupby('time.month').max() - ds.t2m.groupby('time.month').min()
plt.figure(figsize=(10, 6))
plt.pcolormesh(longitude, latitude, trange.values[0, :, :], cmap='viridis')
plt.title('Temperature range')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.colorbar(label='Temperature (°C)')
plt.show();
Questions:
Open the precipitation file and explore it. The units of monthly precipitation are wrongly labeled (unfortunately). They should read: m per day.
dp = xr.open_dataset('ERA5_LowRes_Monthly_tp.nc')
Using .groupby()
, compute the average daily precipitation for each month of the year (I expect a variable of dimensions (month: 12, latitude: 241, longitude: 480)). Convert the units to mm per day. Plot a map of average daily precipitation in January and in August with the levels [0.5, 1, 2, 3, 4, 5, 7, 10, 15, 20, 40]
and the colormap `YlGnBu'
avg_prep_dm = dp.tp.groupby('time.month')
## Conversion to mm
avg_prep_dm = avg_prep_dm.mean() / 1e-3
avg_prep_jan = avg_prep_dm.sel(month=1)
levels = [0.5, 1, 2, 3, 4, 5, 7, 10, 15, 20, 40]
ax = plt.axes(projection=ccrs.Robinson())
avg_prep_jan.plot(ax=ax, transform=ccrs.PlateCarree(), levels=levels, cmap='YlGnBu', cbar_kwargs={'label': 'mm per day'})
ax.coastlines(); ax.gridlines()
ax.set_title('Average daily precipitation in January');
avg_prep_aug = avg_prep_dm.sel(month=7)
ax = plt.axes(projection=ccrs.Robinson())
avg_prep_aug.plot(ax=ax, transform=ccrs.PlateCarree(), levels=levels, cmap='YlGnBu', cbar_kwargs={'label': 'mm per day'})
ax.coastlines(); ax.gridlines()
ax.set_title('Average daily precipitation in August');
Questions:
Open the file containing the surface winds (u10
and v10
) and sea-level pressure (msl
).
dw = xr.open_dataset('ERA5_LowRes_Monthly_uvslp.nc')
Compute [¯SLP] (the temporal and zonal average of sea-level pressure). Convert it to hPa, and plot it (line plot). With the help of plt.axhline, add the standard atmosphere pressure line to the plot to emphasize high and low pressure regions. Repeat with [¯u10] and [¯v10] (in m s−1) and add the 0 horizontal line to the plot (to detect surface westerlies from easterlies for example).
slp = dw.msl.mean(dim=['time', 'longitude']) / 100
slp.plot(label='slp')
plt.axhline(y=1013.25, c='red', label='standard atmosphere pressure')
plt.legend(loc='lower right')
plt.ylabel('sea-level pressure [hPa]')
plt.title('The temporal and zonal average of sea-level pressure (slp)');
avg_u10 = dw.u10.mean(dim=['time', 'longitude'])
avg_v10 = dw.v10.mean(dim=['time', 'longitude'])
avg_u10.plot(label='u-component')
avg_v10.plot(label='v-component')
plt.axhline(c='black')
plt.legend(loc='upper right')
plt.ylabel('wind speed [ms**-1]')
plt.title('The temporal and zonal change of wind speed');
Questions:
The strength and the direction of the winds is parallel to the low and high pressure systems because of the compensatory effect of those winds. But due to Coriolis force they are a little bit shifted.
Download the global average CO2 concentration timeseries data in the CSV format (source: NOAA). Here, let me help your read them using pandas:
df = pd.read_csv('co2_mm_gl.csv', skiprows=55, parse_dates={'date' : [0, 1]}, index_col='date')
df = df.set_axis (["decimal", "average", "average_unc", "trend", "trend_unc"], axis='columns')
df.index = pd.to_datetime(df.index)
df
/tmp/ipykernel_6367/2384463007.py:1: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format. df = pd.read_csv('co2_mm_gl.csv', skiprows=55, parse_dates={'date' : [0, 1]}, index_col='date')
decimal | average | average_unc | trend | trend_unc | |
---|---|---|---|---|---|
date | |||||
1980-06-01 | 1980.458 | 339.99 | 0.09 | 339.22 | 0.07 |
1980-07-01 | 1980.542 | 338.44 | 0.16 | 339.28 | 0.07 |
1980-08-01 | 1980.625 | 337.21 | 0.19 | 339.50 | 0.06 |
1980-09-01 | 1980.708 | 337.05 | 0.12 | 339.44 | 0.06 |
1980-10-01 | 1980.792 | 337.83 | 0.07 | 339.17 | 0.07 |
... | ... | ... | ... | ... | ... |
2023-06-01 | 2023.458 | 419.57 | 0.10 | 418.98 | 0.06 |
2023-07-01 | 2023.542 | 417.85 | 0.10 | 419.14 | 0.06 |
2023-08-01 | 2023.625 | 416.59 | 0.10 | 419.41 | 0.06 |
2023-09-01 | 2023.708 | 416.99 | 0.10 | 419.80 | 0.06 |
2023-10-01 | 2023.792 | 418.64 | 0.10 | 420.20 | 0.06 |
521 rows × 5 columns
Prepare three plots:
df['average'].plot()
plt.ylabel(r'CO$_2$ concentration [ppm]')
plt.xlabel('Year')
plt.title(r'Monthly global CO$_2$ concentration');
df['average'].groupby(df.index.year).mean().plot()
plt.ylabel(r'CO$_2$ concentration [ppm]')
plt.xlabel('Year')
plt.title(r'Global annual average of the CO$_2$ concentration');
fig, ax = plt.subplots()
c = df['average'].groupby(df.index.year).mean()
x = np.arange(df.index.year.min(), df.index.year.max()+1)
ax.plot(x, c, label=r'CO$_2$ concentration')
plt.ylabel(r'CO$_2$ concentration [ppm]')
plt.xlabel('Year')
plt.legend()
axr = ax.twinx()
t = ds.t2m.mean(dim=['latitude', 'longitude']).groupby('time.year').mean()
y = np.arange(ds.time.dt.year.min(), ds.time.dt.year.max()+1)
axr.plot(y, t, color='C1', label='2m temperature')
plt.ylabel('Temperature [K]')
plt.legend(loc='lower right')
plt.title(r'Global annual average of the CO$_2$ concentration and temperature');
Questions:
#Inrease between 1980-1985
d = (df.index.year == 1980) | (df.index.year == 1981) | (df.index.year == 1982) | (df.index.year == 1983) | (df.index.year == 1984) | (df.index.year == 1985)
conc1 = df['average_unc'][d].mean()/6
conc1
0.016417910447761194
#Increase between 2016-2021
d = (df.index.year == 2016) | (df.index.year == 2017) | (df.index.year == 2018) | (df.index.year == 2019) | (df.index.year == 2020) | (df.index.year == 2021)
conc2 = df['average_unc'][d].mean()/6
conc2
0.016736111111111108
As of November 2023, the world is currently experiencing El Niño conditions, with projections indicating its persistence through the upcoming spring (source). To understand the implications and compare with La Niña, we will utilize our available data.
Using your learned skills, you should create global anomalie maps of sea-surface temperature, precipitation, and air temperature, comparing one El Niño with one La Niña year. Describe the anomaly patterns you are seeing in your plots (e.g. where do we see an increase/decrease in precipitation).
We suggest to look for literature or a google search on strong ENSO events in the past 40 years, and select one good example for a positive and a negative phase. With citation or links, explain why you picked these years as examples.
As sources state, the last past three winters have shown conditions of La Niña, wherease this year is experiencing some pretty stong conditions of El Niño, thus warmer temperatures.
We will now procede by analyzing and comparing these two situations, showing that the effects are spreaded worldwide. The following picture shows the apparences of El Niño over the years:
Records state that the Years with the strongest el nino conditions in the last 40 years have been between 1982-83, 1997–98 and 2014–16. On the other hand, between the years 1988-1989, 1998-2001 and 2010-2011 we had strong La Niña conditions. We will analyze the episodes of 2015 for El Niño and 1999 for La Niña. Sea surface temperatures are a usefull tool to show wether we have one condition or another, since El Niño shows warmer than average sea temperatures and La Niña colder than average.
st = xr.open_dataset('ERA5_LowRes_Monthly_sst.nc')
start_date_o = pd.to_datetime('2015-01-01')
end_date_o = pd.to_datetime('2015-12-31')
# Selecting data for El Niño using boolean indexing
st_o = st.sel(time=slice(start_date_o, end_date_o))
start_date_a = pd.to_datetime('1999-01-01')
end_date_a = pd.to_datetime('1999-12-31')
# Selecting data for La Niña using boolean indexing
st_a = st.sel(time=slice(start_date_a, end_date_a))
st_o_avg = st_o.sst.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
st_o_avg.plot(ax=ax, transform=ccrs.PlateCarree(), levels=[270, 275, 280, 285, 290, 295, 300, 305], cbar_kwargs={'label':'Sea surface temperature [K]'})
ax.coastlines(); ax.gridlines();
st_a_avg = st_a.sst.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
st_a_avg.plot(ax=ax, transform=ccrs.PlateCarree(), levels=[270, 275, 280, 285, 290, 295, 300, 305], cbar_kwargs={'label':'Sea surface temperature [K]'})
ax.coastlines(); ax.gridlines();
As we can see from these two plots, there is a sea surface temperature difference mostly in the Pacific Ocean, where there is colder water during La Niña conditions. In the other parts of the world we cannot see such strong differences.
#comparing the difference in sst for the El Niño year with the mean sst of La Niña
sst_anomaly_o = st_o['sst'] - st_a_avg
sst_anomaly_o = sst_anomaly_o.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
sst_anomaly_o.plot.imshow(ax=ax, transform=ccrs.PlateCarree(), levels=20, cbar_kwargs={'label':'Sea surface temperature difference'})
ax.coastlines(); ax.gridlines();
#comparing the difference in sst for La Niña year with the mean sst of El Niño
sst_anomaly_a = st_a['sst'] - st_o_avg
sst_anomaly_a = sst_anomaly_a.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
sst_anomaly_a.plot.imshow(ax=ax, transform=ccrs.PlateCarree(), levels=20, cbar_kwargs={'label':'Sea surface temperature difference'})
ax.coastlines(); ax.gridlines();
The sea surface temperature anomalies show clearly that El Niño has warmer temperatures as compared with La Niña.
# Selecting data for El Niño using boolean indexing
pre_o = dp.sel(time=slice(start_date_o, end_date_o))
# Selecting data for La Niña using boolean indexing
pre_a = dp.sel(time=slice(start_date_a, end_date_a))
pre_o_avg = pre_o.tp.mean(dim='time')/ 1e-3 #converting to mm per day
ax = plt.axes(projection=ccrs.Robinson())
pre_o_avg.plot(ax=ax, transform=ccrs.PlateCarree(), levels=[0,5,10,15,20,30,50], cbar_kwargs={'label':'Total precipitation [mm per day]'})
ax.coastlines(); ax.gridlines();
pre_a_avg = pre_a.tp.mean(dim='time')/ 1e-3 #converting to mm per day
ax = plt.axes(projection=ccrs.Robinson())
pre_a_avg.plot(ax=ax, transform=ccrs.PlateCarree(), levels=[0,5,10,15,20,30,50], cbar_kwargs={'label':'Total precipitation [mm per day]'})
ax.coastlines(); ax.gridlines();
We see higher precipitation over the eastern Pacific Ocean when looking at El Niño conditions; this makes sense since during El Niño the southern jet stream strengthens especially across the eastern Pacific Ocean more moisture is allowed to be transported onshore, resulting in more precipitation.
#comparing the difference in tp for the El Niño year with the mean tp of La Niña
pre_anomaly_o = pre_o['tp']/1e-3 - pre_a_avg
pre_anomaly_o = pre_anomaly_o.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
pre_anomaly_o.plot.imshow(ax=ax, transform=ccrs.PlateCarree(), levels=20, cbar_kwargs={'label':'Total precipitation difference'})
ax.coastlines(); ax.gridlines();
#comparing the difference in tp for La Niña year with the mean tp of El Niño
pre_anomaly_a = pre_a['tp']/1e-3 - pre_o_avg
pre_anomaly_a = pre_anomaly_a.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
pre_anomaly_a.plot.imshow(ax=ax, transform=ccrs.PlateCarree(), levels=20, cbar_kwargs={'label':'Total precipitation difference'})
ax.coastlines(); ax.gridlines();
Also these two plots confirm the fact that El Niño brings more preciitation, mostly in the equatorial pacific Ocean.
# Selecting data for El Niño using boolean indexing
t_o = ds.sel(time=slice(start_date_o, end_date_o))
# Selecting data for La Niña using boolean indexing
t_a = ds.sel(time=slice(start_date_a, end_date_a))
t_o_avg = t_o.t2m.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
t_o_avg.plot(ax=ax, transform=ccrs.PlateCarree(), levels=[220, 230, 240, 250, 260, 270, 280, 290, 300], cbar_kwargs={'label':'2 m temperature [K]'})
ax.coastlines(); ax.gridlines();
t_a_avg = t_a.t2m.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
t_a_avg.plot(ax=ax, transform=ccrs.PlateCarree(), levels=[220, 230, 240, 250, 260, 270, 280, 290, 300], cbar_kwargs={'label':'2 m temperature [K]'})
ax.coastlines(); ax.gridlines();
Once again we see that when we are in a period of El Niño conditions, the temperature over low latitudes is higher than average; on higher latitudes we cannot see this effect still.
#comparing the difference in t2m for the El Niño year with the mean sst of La Niña
t_anomaly_o = t_o['t2m'] - t_a_avg
t_anomaly_o = t_anomaly_o.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
t_anomaly_o.plot.imshow(ax=ax, transform=ccrs.PlateCarree(), levels=20, cbar_kwargs={'label':'2 m temperature difference'})
ax.coastlines(); ax.gridlines();
#comparing the difference in t2m for La Niña year with the mean sst of El Niño
t_anomaly_a = t_a['t2m'] - t_o_avg
t_anomaly_a = t_anomaly_a.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
t_anomaly_a.plot.imshow(ax=ax, transform=ccrs.PlateCarree(), levels=20, cbar_kwargs={'label':'2 m temperature difference'})
ax.coastlines(); ax.gridlines();
Also in this case we can state what we said so far, La Niña is characterized by colder temperatures respectively to an El Niño period.