Group project: the Climate System¶

Students: Linhard Rosina, Zappini Carolin

In [1]:
# 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

Part 1 - temperature climatology¶

Open the ERA5 temperature data:

In [2]:
ds = xr.open_dataset('ERA5_LowRes_Monthly_t2m.nc')
  • Compute and plot the temporal mean temperature $\overline{T}$ for the entire period (unit °C)
In [3]:
#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');
  • Compute and plot $\overline{T^{*}}$ (see lesson), the zonal anomaly map of average temperature.
In [4]:
# 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');
  • Compute and plot the monthly average temperature for each month $\overline{T_M}$ (annual cycle). I expect a variable of dimensions (month: 12, latitude: 241, longitude: 480). Hint: remember the .groupby() command we learned in the lesson. Now plot the average monthly temperature range, i.e. $\overline{T_M}max$ - $\overline{T_M}min$ on a map.
In [5]:
#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();
In [6]:
# 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:

  1. Look at the zonal temperature anomaly map.
    • Explain why northern Europe and the North Atlantic region is significantly warmer than the same latitudes in North America or Russia.
    • Explain why the Northern Pacific Ocean does not have a similar pattern.
  • Northern Europe and the North Atlantic region is significantly warmer than the same latitudes in North America or Russia due to the thermohaline circulation, which brings heat from the equatorial region of the gulf of mexico through oceanic currents. This circulation is formed by deep-ocean currents that are driven by differences in the water's density, which is controlled by temperature (thermo) and salinity (haline).
  • The Northern pacific ocean does not have a similar pattern because the deep water currents flowing there are mainly cold.

  1. Look at the average monthly temperature range map.
    • Explain why the temperature range is smaller in the tropics than at higher latitudes
    • Explain why the temperature range is smaller over oceans than over land
    • Where is the temperature range largest? Explain why.
  • The temperature range is smaller in the tropics than at higher latitudes because tropical regions recieve perpendicular solar energy at noon almost constantly throughout the year, meaning that seasonal changes are less pronounced than higher latitudes.
  • The temperature range is also smaller over oceans than over land because oceans can retain heat better than land, releasing it during cold periods. In other words oceans have higher heat capacity, keeping their temperature within a certain range.
  • The temperature range is at its largest in The Northern Hemisphere over land and at the North pole. This is a direct consequence of the facts stated above: higher latitudes recieve solar radiation at different angles and at different intensities over the year and the Northern Hemisphere has more land than the Southern Hemisphere (land has much less heat capacity than oceans).

Part 2 - Precipitation climatology¶

Open the precipitation file and explore it. The units of monthly precipitation are wrongly labeled (unfortunately). They should read: m per day.

In [7]:
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'

In [8]:
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');
In [9]:
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:

  1. Describe the location of the ITCZ in January and August. Without going into the details, explain (in one or two sentences)
  2. Describe the precipitation seasonality in West Africa and in India. Name the phenomenon at play.
  1. In January the ITCZ is located north of the equator in South America, the Atlantic and eastern part of the Pacific Ocean, but south of the equator from Africa in eastern direction up to the western part of the Pacific. In August the ITCZ moves more north: as we can see there is more precipitation in India and China than in January.
  2. In August there's the rainy time in India and West Africa. In India the phenomenon is called Monsoon, which lasts fron June to September. On the other hand, the rain season in West Africa lasts longer, namely from May to September.

Part 3: sea-level pressure and surface winds¶

Open the file containing the surface winds (u10 and v10) and sea-level pressure (msl).

In [10]:
dw = xr.open_dataset('ERA5_LowRes_Monthly_uvslp.nc')

Compute $\left[ \overline{SLP} \right]$ (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 $\left[ \overline{u_{10}} \right]$ and $\left[ \overline{v_{10}} \right]$ (in m s$^{-1}$) and add the 0 horizontal line to the plot (to detect surface westerlies from easterlies for example).

In [11]:
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)');
In [12]:
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:

  1. Based on your knowledge about the general circulation of the atmosphere, explain the latitude location of the climatological high and low pressure systems of Earth.
  2. Similarly, explain the direction and strength of the zonal and meridional winds on earth (tip: the sea-level pressure plot helps)
  1. From south to north: the first and deepest low pressure system is the sub-polar low, the subtropical high at around 30° follows and then at the equator is the low pressure system of the ITCZ. On the northern hemisphere it is the other way round with weaker intensities because of more landmass.
  2. The strongest winds are the westerlies, which equals the strong low pressure around the Antartica and also the northerly wind coming from the subtropical high system zone. Little bit south of the equator the wind is coming from south and a bit north of the equator from north. The zonal winds are coming from the East in the tropics due to the Coriolis force. This are the trade winds.

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.

Part 4: temperature change and CO$_2$ concentrations¶

Download the global average CO$_2$ concentration timeseries data in the CSV format (source: NOAA). Here, let me help your read them using pandas:

In [13]:
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')
Out[13]:
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:

  • plot the monthly global CO$_2$ concentration as a function of time.
In [14]:
df['average'].plot()
plt.ylabel(r'CO$_2$ concentration [ppm]')
plt.xlabel('Year')
plt.title(r'Monthly global CO$_2$ concentration');
  • plot the annual average timeseries of global CO$_2$ concentration as a function of time.
In [15]:
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');
  • plot the annual average timeseries of global CO$_2$ concentration and of global 2m temperature from ERA5 on the same plot (using a secondary y axis for temperature).
In [16]:
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:

  1. Describe and explain the annual cycle of CO$_2$ concentrations
  2. What was the CO$_2$ concentration in the atmosphere in the pre-industrial era? Compute the annual increase in CO$_2$ concentration (unit: ppm per year) between 1980 and 1985 and between 2016 and 2021.
  3. Describe the relationship between global temperatures and CO$_2$ concentrations. Beside CO$_2$, name three processes that can influence temperature variability and change at the global scale.
  1. In the annual cycle you see the impact of the vegetational period on the northern hemisphere. In the spring an summer the CO$_2$ concetration is decreasing when the plants start growing and in autumn and winter it increases again because they loose their leaves and as a consequence they do not make much photosynthesis. However over the time the total amount of CO$_2$ is increasing.
  2. In the pre-industrial era the CO$_2$ concentration was 278 ppm.
In [17]:
#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
Out[17]:
0.016417910447761194
In [18]:
#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
Out[18]:
0.016736111111111108
  1. The temperature trend is the same as the trend of the CO$_2$ trend, but the temperature curve isn't that flat because of the impact of other processes like El Niño, big eruptions of vulcanos and the sun cycles.

Part 5: variability and ENSO (open research question)¶

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.

Sea-surface temperature¶

In [19]:
st = xr.open_dataset('ERA5_LowRes_Monthly_sst.nc')
In [20]:
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))
In [21]:
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))
  • El Niño
In [22]:
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();
  • La Niña
In [23]:
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.

Sea surface temperature anomalies¶

  • El Niño conditions
In [24]:
#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();
  • La Niña conditions
In [25]:
#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.

Total precipitation map¶

In [26]:
# Selecting data for El Niño using boolean indexing
pre_o = dp.sel(time=slice(start_date_o, end_date_o)) 
In [27]:
# Selecting data for La Niña using boolean indexing
pre_a = dp.sel(time=slice(start_date_a, end_date_a))
  • El Niño
In [28]:
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();
  • La Niña
In [29]:
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.

Total precipitation anomalies¶

  • El Niño
In [30]:
#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();
In [31]:
#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.

Air temperature¶

In [32]:
# Selecting data for El Niño using boolean indexing
t_o = ds.sel(time=slice(start_date_o, end_date_o))
In [33]:
# Selecting data for La Niña using boolean indexing
t_a = ds.sel(time=slice(start_date_a, end_date_a))
  • El Niño
In [34]:
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();
  • La Niña
In [35]:
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.

Air temperature anomalies¶

  • El Niño
In [36]:
#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();
  • La Niña
In [37]:
#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.

In [ ]: