Übertsroider, Wolf
Objectives
In this final project, you will apply the methods you learned over the past weeks to answer the questions below.
Deadline
Please submit your project via OLAT before Thursday January 11 at 00H (in the night from Wednesday to Thursday).
Formal requirements
You will work in groups of two. If we are an odd number of students, one group can have three participants. (Tip: I recommend that students who have not followed a programming class to team up with students who have).
Each group will submit one (executed) jupyter notebook containing the code, plots, and answers to the questions (text in the markdown format). Please also submit an HTML version of the notebook. Each group member must contribute to the notebook. The notebook should be self-contained and the answers must be well structured. The plots must be as understandable as possible (title, units, x and y axis labels, appropriate colors and levels…).
Please be concise in your answer. We expect a few sentences per answer at most - there is no need to write a new text book in this project! Use links and references to the literature or your class slides where appropriate.
Grading
We will give one grade per project, according to the following table (total 10 points):
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
import pandas as pd
import cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
plt.rcParams['figure.figsize'] = (12, 5)
Open the ERA5 temperature data:
ds = xr.open_dataset('../Final_Project/ERA5_LowRes_Monthly_t2m.nc')
ds
<xarray.Dataset> Dimensions: (longitude: 480, latitude: 241, time: 480) Coordinates: * longitude (longitude) float32 -179.6 -178.9 -178.1 ... 178.1 178.9 179.6 * latitude (latitude) float32 90.0 89.25 88.5 87.75 ... -88.5 -89.25 -90.0 * time (time) datetime64[ns] 1979-01-01 1979-02-01 ... 2018-12-01 Data variables: t2m (time, latitude, longitude) float32 ... Attributes: Conventions: CF-1.6 history: 2019-11-18 09:36:58 GMT by grib_to_netcdf-2.14.0: /opt/ecmw...
Plot three global maps:
.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.Questions:
That is because the gulf stream brings warm water an thus warm air from the tropics into higher latitudes. This affects the temperatures in Northern Europe.
Compared to the gulf stream in the North Atlantic the Northern Pacific Ocean doesn't have such a strong warm stream. There is a warm stream in the Northern Pacific Ocean called the Kurohio current but it doesn't reach such high latitudes and thus has less influence on temperatures at higher latitudes than the gulf strem.
That is because the tropics recieve more direct and concentrated solar radiation. This is because the sun is nearly overhead throughout the year, wich leads to consistend day lenghts and heating. Throug that heating the temperature stays consistend over the year (there are also no clearly defined seasons) so the temperature range is samaller.
The temperature range over water is samller than over land because land heats and cools easier than water does. This is because of the high specific heat capacity of water Therefore the watertemperature doesn't change strongly
The temperature range is largest in siberia, that results from a combination of arctic influences, continental climate, snow cover and the geographic location. Siberia is located at high latitudes, this results in a more pronounced difference between summer and winter temperatures due to the tilt of the Earth's axis.
fig = plt.figure()
ax = plt.axes(projection=ccrs.Robinson())
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor='black',linestyle=':' )
temp_mean_C = ds['t2m'].mean(dim = 'time').values-273.15
contour = ax.contourf(ds['longitude'], ds['latitude'], temp_mean_C, transform=ccrs.PlateCarree())
gl = ax.gridlines(draw_labels=True)
cbar = plt.colorbar(contour, ax=ax)
cbar.set_label('Temperature in °C')
ax.set_title('Temporal mean Temperature')
plt.show()
zonal_anomaly = ds['t2m'].mean(dim='time') - ds['t2m'].mean(dim=['time', 'longitude'])
fig = plt.figure()
ax = plt.axes(projection=ccrs.Robinson())
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor='black',linestyle=':' )
levels = np.arange(zonal_anomaly.min().round()-1,zonal_anomaly.max().round()+2,2)
contour = ax.contourf(ds['longitude'], ds['latitude'], zonal_anomaly, transform=ccrs.PlateCarree(),levels = levels)
gl = ax.gridlines(draw_labels=True)
cbar = plt.colorbar(contour, ax=ax)
cbar.set_label('Mean Temperature in °C')
ax.set_title('Zonal temperature anomaly')
plt.show()
#compute annual cylcle
month_aver_temp = ds['t2m'].groupby('time.month').mean()
x = month_aver_temp['month']
y = month_aver_temp.mean('longitude').mean('latitude')-273.15
plt.xlabel('Month')
plt.ylabel('Average Temperature in °C')
plt.title('Monthly average Temperature')
plt.xticks(x)
plt.plot(x,y)
plt.show()
fig = plt.figure()
ax = plt.axes(projection=ccrs.Robinson())
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor='black',linestyle=':' )
#range
temp_range = (ds['t2m'].groupby('time.month').max()-ds['t2m'].groupby('time.month').min()).mean('month')
contour = ax.contourf(ds['longitude'], ds['latitude'], temp_range, transform=ccrs.PlateCarree())
gl = ax.gridlines(draw_labels=True)
cbar = plt.colorbar(contour, ax=ax)
cbar.set_label('Temperature in °C')
ax.set_title('Average monthly temperature range')
plt.show()
Open the precipitation file and explore it. The units of monthly precipitation are wrongly labeled (unfortunately). They should read: m per day.
ds_prec = xr.open_dataset('../Final_Project/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'
Questions:
In January, the Intertropical Convergence Zone (ITCZ) is generally situated near or over the equator. By August, it tends to shift northward, reaching a position closer to the northern hemisphere, due to the seasonal movement of the sun. (We assume you have a typo in your question and meant January and Agust because of the plots)
West Africa experiences more precipitation in August than in June, this is called the West African Monsoon. This happens because the (ITCZ) shifts northward and brings heavy rains.
In India it's a similar pattern and is known as Indian Monsoon.
average_prec = ds_prec['tp'].groupby('time.month').mean()
average_prec_in_mm = average_prec *1000
average_prec_in_mm_grouped = average_prec_in_mm.groupby('month')
#create map
fig = plt.figure()
ax = plt.axes(projection=ccrs.Robinson())
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor='black',linestyle=':' )
gl = ax.gridlines(draw_labels=True)
levels = (0.5, 1, 2, 3, 4, 5, 7, 10, 15, 20, 40)
#plot january
contour_prec_jan = ax.contourf(ds['longitude'],ds['latitude'],average_prec_in_mm_grouped[1], transform=ccrs.PlateCarree(), levels = levels, cmap = 'YlGnBu')
cbar = plt.colorbar(contour_prec_jan, ax=ax)
cbar.set_label('Precipitation in mm/day')
ax.set_title('Daily average precipitation January')
#create map
fig = plt.figure()
ax = plt.axes(projection=ccrs.Robinson())
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor='black',linestyle=':' )
gl = ax.gridlines(draw_labels=True)
#plot august
contour_prec_aug = ax.contourf(ds['longitude'],ds['latitude'],average_prec_in_mm_grouped[8], transform=ccrs.PlateCarree(), levels = levels, cmap = 'YlGnBu')
cbar = plt.colorbar(contour_prec_aug, ax=ax)
cbar.set_label('Precipitation in mm/day')
ax.set_title('Daily average precipitation August')
plt.show()
Open the file containing the surface winds (u10
and v10
) and sea-level pressure (msl
).
ds_winds_and_msl = xr.open_dataset('../Final_Project/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).
Questions:
As we can see at -30° and 30° we have high pressure systems wich are known as the subtropical highs (between hadley cell and ferrell cell)
At -60° and 60° we can see low pressure systems (the one at -60° is way stronger because there is not that much landmass in the south) wich are known as the subpolar lows
(between ferrell cell and polar cell)
At the poles we usually have high pressure systems wich are very weak
Zonal winds move along east west. They arise at the equator due to warm rising air at the ITCZ generating lows resulting into east west trade winds. In the subtropics descending air from the hadley cell forms subtropical highs contributing to trade winds. In mid-latitudes westerlies prevail due to ferrell cell circulation.
Meridional winds move along north south. More significant flow occurs, especially in regions with strong pressure gradients, such as the polar front. In polar regions meridional flow is influenced by the polar easterlies and westerlies. These winds arise from diffrences in sea-level pressure, shaped by solar heating and influenced by the Coriolis effect induced by the Earth's rotation.
#temporal average
temporal_average = (ds_winds_and_msl['msl'].mean('time'))/100
#zonal average
zonal_average = ( ds_winds_and_msl['msl'].mean(['longitude','time']))/100
y1 = temporal_average
y2 = zonal_average
x = ds_winds_and_msl['latitude']
plt.plot(x,y1,color = 'Blue')
plt.plot(x,y2,color = 'Red')
plt.title('Zonal average (Red) and Temporal average (Blue) of sea level pressure')
plt.ylabel('Pressure in hPa')
plt.xlabel('Latitude')
plt.axhline(1013.25,color='Green', linestyle='--', label = 'Standard atmospher pressure (hPa)')
# Add the value on the y-axis
plt.text(x.min()-10, 1013.25, f'{1013.25}', va='center', ha='right', color='Green')
plt.legend(loc = 'lower right')
plt.show()
#temporal average for u10 and v10
temporal_average_u10 = ds_winds_and_msl['u10'].mean('time')
temporal_average_v10 = ds_winds_and_msl['v10'].mean('time')
#zonal average for u10 and v10
zonal_average_u10 = ds_winds_and_msl['u10'].mean(['longitude', 'time'])
zonal_average_v10 = ds_winds_and_msl['v10'].mean(['longitude', 'time'])
#plotting u10
plt.plot(ds_winds_and_msl['latitude'], temporal_average_u10, color='Blue')
plt.plot(ds_winds_and_msl['latitude'], zonal_average_u10, color='Red')
plt.title('Zonal average (Red) and Temporal average (Blue) Wind Components (u10)')
plt.ylabel('Wind Speed (m/s)')
plt.xlabel('Latitude')
plt.axhline(0, color='Green', linestyle='--', label='Zero Wind Line')
plt.legend()
plt.show()
#plotting v10
plt.plot(ds_winds_and_msl['latitude'], temporal_average_v10, color='Blue')
plt.plot(ds_winds_and_msl['latitude'], zonal_average_v10, color='Red')
plt.title('Zonal average (Red) and Temporal average (Blue) Wind Components (v10)')
plt.ylabel('Wind Speed (m/s)')
plt.xlabel('Latitude')
plt.axhline(0, color='Green', linestyle='--', label='Zero Wind Line')
plt.legend()
plt.show()
Download the global average CO$_2$ concentration timeseries data in the CSV format (source: NOAA). Here, let me help your read them using pandas:
df = pd.read_csv('../Final_Project/co2_mm_gl.csv', skiprows=38, parse_dates={'date' : [0, 1]}, index_col='date',date_format = '%Y %m')
df
# we canged the skiprows to 38 to use all data and get a reasonable head
decimal | average | average_unc | trend | trend_unc | |
---|---|---|---|---|---|
date | |||||
1979-01-01 | 1979.042 | 336.56 | 0.11 | 335.92 | 0.09 |
1979-02-01 | 1979.125 | 337.29 | 0.09 | 336.25 | 0.10 |
1979-03-01 | 1979.208 | 337.88 | 0.11 | 336.51 | 0.10 |
1979-04-01 | 1979.292 | 338.32 | 0.12 | 336.72 | 0.10 |
1979-05-01 | 1979.375 | 338.26 | 0.03 | 336.71 | 0.10 |
... | ... | ... | ... | ... | ... |
2023-05-01 | 2023.375 | 420.54 | 0.10 | 418.87 | 0.06 |
2023-06-01 | 2023.458 | 419.53 | 0.10 | 418.94 | 0.06 |
2023-07-01 | 2023.542 | 417.83 | 0.10 | 419.10 | 0.06 |
2023-08-01 | 2023.625 | 416.53 | 0.10 | 419.36 | 0.06 |
2023-09-01 | 2023.708 | 416.83 | 0.10 | 419.70 | 0.06 |
537 rows × 5 columns
Prepare three plots:
Questions:
The annual cycle of CO$_2$ concentrations is shaped from the seasons. In spring and early summer when plants beginn to grow they absorb CO$_2$. In late summer and fall plants die or drop leaves what releases CO$_2$. In winter plants barely do photosynthesis so there is no absorbtion of CO$_2$. The annual cycle is more pronounced in the Northern Hemisphere due to the larger landmasses and vegetation cover. Of course humanity has a large impact on the annual CO$_2$ conentration by combusting fossil fuels.
In the pre-industrial era in the mid-1700s, atmospheric carbon dioxide was 280 ppm or less.
The annual increase in CO2 between 1980 and 1985 is 1.3251666666666666 ppm per year The annual increase in CO2 between 2021 and 2016 is 2.3273333333333426 ppm per year
Increased concentrations of CO$_2$ in the atmosphere enhance the greenhouse effect, leading to more heat being trapped. This results in a warming of the Earth's surface.
Changes in solar radiation, such as variations in the Sun's output or changes in Earth's orbit, can influence global temperatures. Though these variations are relatively small over short timescales, they can contribute to climate variability.
Ocean currents play a crucial role in redistributing heat around the planet. Changes in ocean circulation, such as El Niño and La Niña events, can significantly impact regional and global temperatures. For instance, El Niño tends to bring warmer conditions, while La Niña can lead to cooler temperatures in some regions
Alterations in land cover, such as deforestation or urbanization, can affect local and regional climates. Changes in land use influence the absorption and reflection of solar radiation, impacting temperatures in the modified areas.
df_1985 = df[df.index.year == 1985]
co2_1985 = df_1985['average'].mean()
df_1980 = df[df.index.year == 1980]
co2_1980 = df_1980['average'].mean()
co2_conc_1980to1985 = (co2_1985 - co2_1980)/5
print('The annual increase in CO2 between 1980 and 1985 is',co2_conc_1980to1985,'ppm per year')
df_2016 = df[df.index.year == 2016]
co2_2016 = df_2016['average'].mean()
df_2021 = df[df.index.year == 2021]
co2_2021 = df_2021['average'].mean()
co2_conc_2016to2021 = (co2_2021 - co2_2016)/5
print('The annual increase in CO2 between 2021 and 2016 is',co2_conc_2016to2021, 'ppm per year')
The annual increase in CO2 between 1980 and 1985 is 1.3251666666666666 ppm per year The annual increase in CO2 between 2021 and 2016 is 2.3273333333333426 ppm per year
date = df.index
conc = df['average']
#plot
plt.plot(date, conc,label='Monthly CO$_2$ Concentration')
plt.title('Monthly global CO$_2$ concentration')
plt.xlabel('Year')
plt.ylabel('CO$_2$ concentration in ppm')
plt.legend()
plt.show()
# Calculate annual averages
annual_avg = df['average'].resample('A').mean().reset_index()
#plot
plt.figure(figsize=(12, 6))
plt.plot(annual_avg['date'], annual_avg['average'], label='Annual Average CO$_2$ Concentration', marker='o')
plt.xlabel('Year')
plt.ylabel('CO$_2$ Concentration (ppm)')
plt.title('Annual Average Global CO$_2$ Concentration Over Time')
plt.legend()
plt.show()
co2_annual = df['average'].resample('A').mean()
temp_annual = ds['t2m'].resample(time='A').mean(dim='time')
#calculate the global average temperature
global_temp_annual = temp_annual.mean(dim=['latitude', 'longitude'])-273.15
#plot
fig, ax1 = plt.subplots(figsize=(12, 5))
#CO2 concentration plot
ax1.set_xlabel('Year')
ax1.set_ylabel('CO$_2$ Concentration (ppm)')
ax1.plot(co2_annual.index.year, co2_annual, label = 'CO$_2$ Concentration', marker ='o', markeredgewidth = 0.3, markeredgecolor = 'Black')
#create second axis
ax2 = ax1.twinx()
ax2.set_ylabel('Temperature (°C)')
ax2.plot(global_temp_annual['time.year'], global_temp_annual,color='Red', label = 'Temperature', marker = 'o',markeredgewidth = 0.3, markeredgecolor = 'Black')
plt.title('Annual Average Timeseries of Global CO$_2$ Concentration and 2m Temperature (ERA5)')
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')
plt.show()
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.
ds_sst = xr.open_dataset('../Final_Project/ERA5_LowRes_Monthly_sst.nc')
commpared events:
el nino 1997
la nina 2010
We picked those two years because there was a very strong el nino and la nina event and those are the closest to today (we also tought about picking el nino of 1982 but we decided it is to far in the past)
We got a brief overview over the events on this chart https://de.wikipedia.org/wiki/El_Ni%C3%B1o-Southern_Oscillation#/media/Datei:SOI.svg
ds_sst_filtered_nino = ds_sst.sel(time=slice('1997-01-01', '1998-12-31')).mean(dim='time')
ds_sst_filtered_nina = ds_sst.sel(time=slice('2010-01-01', '2011-12-31')).mean(dim='time')
#calculate anomalie
ds_sst_nina_nino = ds_sst_filtered_nino['sst'] - ds_sst_filtered_nina['sst']
#plot the difference
fig = plt.figure()
ax = plt.axes(projection=ccrs.Robinson())
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor='black',linestyle=':' )
gl = ax.gridlines(draw_labels=True)
contour_sst_nina_nino = ax.contourf(ds_sst_filtered_nino['longitude'],ds_sst_filtered_nino['latitude'],ds_sst_nina_nino, transform=ccrs.PlateCarree())
cbar = plt.colorbar(contour_sst_nina_nino, ax=ax)
cbar.set_label('Seasurface temperature difference')
plt.title('Seasurface temperature anomalie')
plt.show()
In the plot Seasurface temperature anomalie we can see a strong temperature rise in the Equatorial Pacific, wich we can connect with an El Niño event. When the temperature difference is negative in a region the temperature rises more in an La Niña event.
ds_prec_filtered_nino = ds_prec.sel(time=slice('1997-01-01', '1998-12-31')).mean(dim='time')*1000
ds_prec_filtered_nina = ds_prec.sel(time=slice('2010-01-01', '2011-12-31')).mean(dim='time')*1000
#calculate anomalie
ds_prec_nina_nino = ds_prec_filtered_nino['tp'] - ds_prec_filtered_nina['tp']
#plot diffrence
fig = plt.figure()
ax = plt.axes(projection=ccrs.Robinson())
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor='black',linestyle=':' )
gl = ax.gridlines(draw_labels=True)
levels = np.arange(0,ds_prec_nina_nino.max()+1,0.5)
contour_prec_nina_nino = ax.contourf(ds_prec_filtered_nino['longitude'],ds_prec_filtered_nino['latitude'],ds_prec_nina_nino, transform=ccrs.PlateCarree(), levels = levels)
cbar = plt.colorbar(contour_prec_nina_nino, ax=ax)
cbar.set_label('Precipitation in mm/day')
plt.title('Precipitation anomalie')
plt.show()
In the plot Precipitation anomalie we can see how precipitation increses with an El Niño event. On the white spots we have negative precipitation wich means we have stronger precipitation in this regions during an La Niña event.
ds_t2m_filtered_nino = ds.sel(time=slice('1997-01-01', '1998-12-31')).mean(dim='time')
ds_t2m_filtered_nina = ds.sel(time=slice('2010-01-01', '2011-12-31')).mean(dim='time')
#calculate anomalie
ds_t2m_nina_nino = ds_t2m_filtered_nino['t2m'] - ds_t2m_filtered_nina['t2m']
#plot the difference
fig = plt.figure()
ax = plt.axes(projection=ccrs.Robinson())
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor='black',linestyle=':' )
gl = ax.gridlines(draw_labels=True)
contour_t2m_nina_nino = ax.contourf(ds_t2m_filtered_nino['longitude'],ds_t2m_filtered_nino['latitude'],ds_t2m_nina_nino, transform=ccrs.PlateCarree())
cbar = plt.colorbar(contour_t2m_nina_nino, ax=ax)
cbar.set_label('Air Temperature')
plt.title('Air temperature anomalie')
plt.show()
In the plot Air temperature anomalie we can see similar patterns as in the seasurface temperature anomalie plot.