Group project: the Climate System¶

Instructions¶

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 09 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: We 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. Please ensure that your HTML file is smaller than 10 MB. This helps us provide you with more detailed and readable feedback.

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):

  • correctness of the code and the plots: content, legends, colors, units, etc. (3 points)
  • quality of the answers: correctness, preciseness, appropriate use of links and references to literature or external resources (5 points)
  • originality and quality of the open research question (2 points)
1
2
3
4
5
6
7
8
9
# 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:

1
2
ds = xr.open_dataset('ERA5_LowRes_Monthly_t2m.nc')
ds
<xarray.Dataset> Size: 444MB
Dimensions:    (longitude: 480, latitude: 241, time: 480)
Coordinates:
  * longitude  (longitude) float32 2kB -179.6 -178.9 -178.1 ... 178.9 179.6
  * latitude   (latitude) float32 964B 90.0 89.25 88.5 ... -88.5 -89.25 -90.0
  * time       (time) datetime64[ns] 4kB 1979-01-01 1979-02-01 ... 2018-12-01
Data variables:
    t2m        (time, latitude, longitude) float64 444MB ...
Attributes:
    Conventions:  CF-1.6
    history:      2019-11-18 09:36:58 GMT by grib_to_netcdf-2.14.0: /opt/ecmw...
xarray.Dataset
    • longitude: 480
    • latitude: 241
    • time: 480
    • longitude
      (longitude)
      float32
      -179.6 -178.9 ... 178.9 179.6
      units :
      degrees_east
      long_name :
      longitude
      array([-179.625, -178.875, -178.125, ...,  178.125,  178.875,  179.625],
            dtype=float32)
    • latitude
      (latitude)
      float32
      90.0 89.25 88.5 ... -89.25 -90.0
      units :
      degrees_north
      long_name :
      latitude
      array([ 90.  ,  89.25,  88.5 , ..., -88.5 , -89.25, -90.  ], dtype=float32)
    • time
      (time)
      datetime64[ns]
      1979-01-01 ... 2018-12-01
      long_name :
      time
      array(['1979-01-01T00:00:00.000000000', '1979-02-01T00:00:00.000000000',
             '1979-03-01T00:00:00.000000000', ..., '2018-10-01T00:00:00.000000000',
             '2018-11-01T00:00:00.000000000', '2018-12-01T00:00:00.000000000'],
            dtype='datetime64[ns]')
    • t2m
      (time, latitude, longitude)
      float64
      ...
      units :
      K
      long_name :
      2 metre temperature
      [55526400 values with dtype=float64]
    • longitude
      PandasIndex
      PandasIndex(Index([-179.625, -178.875, -178.125, -177.375, -176.625, -175.875, -175.125,
             -174.375, -173.625, -172.875,
             ...
              172.875,  173.625,  174.375,  175.125,  175.875,  176.625,  177.375,
              178.125,  178.875,  179.625],
            dtype='float32', name='longitude', length=480))
    • latitude
      PandasIndex
      PandasIndex(Index([  90.0,  89.25,   88.5,  87.75,   87.0,  86.25,   85.5,  84.75,   84.0,
              83.25,
             ...
             -83.25,  -84.0, -84.75,  -85.5, -86.25,  -87.0, -87.75,  -88.5, -89.25,
              -90.0],
            dtype='float32', name='latitude', length=241))
    • time
      PandasIndex
      PandasIndex(DatetimeIndex(['1979-01-01', '1979-02-01', '1979-03-01', '1979-04-01',
                     '1979-05-01', '1979-06-01', '1979-07-01', '1979-08-01',
                     '1979-09-01', '1979-10-01',
                     ...
                     '2018-03-01', '2018-04-01', '2018-05-01', '2018-06-01',
                     '2018-07-01', '2018-08-01', '2018-09-01', '2018-10-01',
                     '2018-11-01', '2018-12-01'],
                    dtype='datetime64[ns]', name='time', length=480, freq=None))
  • Conventions :
    CF-1.6
    history :
    2019-11-18 09:36:58 GMT by grib_to_netcdf-2.14.0: /opt/ecmwf/eccodes/bin/grib_to_netcdf -o /cache/data7/adaptor.mars.internal-1574069554.2755394-7634-27-3cce65d0-8d4e-4274-8d69-b5ccb304cc7f.nc /cache/tmp/3cce65d0-8d4e-4274-8d69-b5ccb304cc7f-adaptor.mars.internal-1574069554.2763371-7634-8-tmp.grib

Plot three global maps:

  • Compute and plot the temporal mean temperature $\overline{T}$ for the entire period (unit °C)
  • Compute and plot $\overline{T^{*}}$ (see lesson), the zonal anomaly map of average temperature.
  • Compute and plot the monthly average temperature for each month $\overline{T_M}$ (annual cycle). We 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 map, i.e. $\max(\overline{T_{M}})$ - $\min(\overline{T_{M}})$ (maximum and minimum over the month dimension).

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.
  2. 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.
1
2
3
4
5
6
# Your answers here
#temporal mean
t2_tavg = ds.t2m.mean(dim='time')
ax = plt.axes(projection=ccrs.Robinson())
t2_tavg.plot(ax=ax, transform=ccrs.PlateCarree())
ax.coastlines(); ax.gridlines();
No description has been provided for this image
1
2
3
4
5
#T* plot
t_avg_dep = t2_tavg - t2_tavg.mean(dim='longitude')
ax = plt.axes(projection=ccrs.Robinson())
t_avg_dep.plot.imshow(ax=ax, transform=ccrs.PlateCarree())
ax.coastlines(); ax.gridlines();
No description has been provided for this image
1
2
3
t_month_mean=ds.t2m.groupby('time.month').mean()
t_range=t_month_mean.max(dim="month")-t_month_mean.min(dim="month")
t_range.plot()
<matplotlib.collections.QuadMesh at 0x1351ea990>
No description has been provided for this image

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. Northern Europe and the North Atlantic region are warmer than regions at similar latitudes in North America and Russia due to several factors. The North Atlantic Drift (a continuation of the Gulf Stream) brings warm ocean currents, moderating the climate. Proximity to large bodies of water, which absorb and release heat slowly, results in more stable temperatures. Prevailing westerly winds carry warm, moist air from the Atlantic, helping to maintain milder winters. Europe's geography, with few large mountain ranges, allows these winds to reach the region easily. In contrast, Siberia and North America are blocked by mountain ranges like the Rocky and Ural Mountains, which trap cold Arctic air and lead to harsher winters.

Explain why the Northern Pacific Ocean does not have a similar pattern. there is the kuroshio current, but the prevailing winds drive it further away from land/ the mainland US is blocked by the rocky mountains.

Look at the average monthly temperature range map. Explain why the temperature range is smaller in the tropics than at higher latitudes at lower latitudes the annual variability of energy balance due to earths tilt is smaller while at higher latitudes the difference is much larger.

Explain why the temperature range is smaller over oceans than over land water has a higher specific heat capacity than land so it stores heat for longer and is also more resistant to warming

Where is the temperature range largest? Explain why. Eastern siberia and Polar North-America surface albedo would probably play a large role in those environments since they are covered by snow during winter so would have a higher albedo, leading to cooler temperatiures, while on the other hand in summer, these landmasses are not covered by snow and can absorb much more solar radiation. both regions can be affected by polar jet streams as well, which could lead to very cold temperatures.

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.

1
2
ds = xr.open_dataset('ERA5_LowRes_Monthly_tp.nc')
ds
<xarray.Dataset> Size: 444MB
Dimensions:    (longitude: 480, latitude: 241, time: 480)
Coordinates:
  * longitude  (longitude) float32 2kB -179.6 -178.9 -178.1 ... 178.9 179.6
  * latitude   (latitude) float32 964B 90.0 89.25 88.5 ... -88.5 -89.25 -90.0
  * time       (time) datetime64[ns] 4kB 1979-01-01 1979-02-01 ... 2018-12-01
Data variables:
    tp         (time, latitude, longitude) float64 444MB ...
Attributes:
    Conventions:  CF-1.6
    history:      2019-11-18 09:30:18 GMT by grib_to_netcdf-2.14.0: /opt/ecmw...
xarray.Dataset
    • longitude: 480
    • latitude: 241
    • time: 480
    • longitude
      (longitude)
      float32
      -179.6 -178.9 ... 178.9 179.6
      units :
      degrees_east
      long_name :
      longitude
      array([-179.625, -178.875, -178.125, ...,  178.125,  178.875,  179.625],
            dtype=float32)
    • latitude
      (latitude)
      float32
      90.0 89.25 88.5 ... -89.25 -90.0
      units :
      degrees_north
      long_name :
      latitude
      array([ 90.  ,  89.25,  88.5 , ..., -88.5 , -89.25, -90.  ], dtype=float32)
    • time
      (time)
      datetime64[ns]
      1979-01-01 ... 2018-12-01
      long_name :
      time
      array(['1979-01-01T00:00:00.000000000', '1979-02-01T00:00:00.000000000',
             '1979-03-01T00:00:00.000000000', ..., '2018-10-01T00:00:00.000000000',
             '2018-11-01T00:00:00.000000000', '2018-12-01T00:00:00.000000000'],
            dtype='datetime64[ns]')
    • tp
      (time, latitude, longitude)
      float64
      ...
      units :
      m
      long_name :
      Total precipitation
      [55526400 values with dtype=float64]
    • longitude
      PandasIndex
      PandasIndex(Index([-179.625, -178.875, -178.125, -177.375, -176.625, -175.875, -175.125,
             -174.375, -173.625, -172.875,
             ...
              172.875,  173.625,  174.375,  175.125,  175.875,  176.625,  177.375,
              178.125,  178.875,  179.625],
            dtype='float32', name='longitude', length=480))
    • latitude
      PandasIndex
      PandasIndex(Index([  90.0,  89.25,   88.5,  87.75,   87.0,  86.25,   85.5,  84.75,   84.0,
              83.25,
             ...
             -83.25,  -84.0, -84.75,  -85.5, -86.25,  -87.0, -87.75,  -88.5, -89.25,
              -90.0],
            dtype='float32', name='latitude', length=241))
    • time
      PandasIndex
      PandasIndex(DatetimeIndex(['1979-01-01', '1979-02-01', '1979-03-01', '1979-04-01',
                     '1979-05-01', '1979-06-01', '1979-07-01', '1979-08-01',
                     '1979-09-01', '1979-10-01',
                     ...
                     '2018-03-01', '2018-04-01', '2018-05-01', '2018-06-01',
                     '2018-07-01', '2018-08-01', '2018-09-01', '2018-10-01',
                     '2018-11-01', '2018-12-01'],
                    dtype='datetime64[ns]', name='time', length=480, freq=None))
  • Conventions :
    CF-1.6
    history :
    2019-11-18 09:30:18 GMT by grib_to_netcdf-2.14.0: /opt/ecmwf/eccodes/bin/grib_to_netcdf -o /cache/data6/adaptor.mars.internal-1574069158.6891544-6829-11-3cc28eae-3777-426d-a5fe-bd3c8ecce585.nc /cache/tmp/3cc28eae-3777-426d-a5fe-bd3c8ecce585-adaptor.mars.internal-1574069158.6900353-6829-4-tmp.grib

Using .groupby(), compute the average daily precipitation for each month of the year (We 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:

  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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Your answers here
tp_month_mean=ds.tp.groupby('time.month').mean(dim='time')
tp_month_mean=tp_month_mean*1000
#plot
levels = [0.5, 1, 2, 3, 4, 5, 7, 10, 15, 20, 40]
cmap = 'YlGnBu'

# Plot the map for January (month = 1) and August (month = 8)
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
january_precip = tp_month_mean.sel(month=1)
august_precip = tp_month_mean.sel(month=8)

# January Plot
january_precip.plot.contourf(ax=axes[0], levels=levels, cmap=cmap, extend='max')
axes[0].set_title('Average Daily Precipitation - January')

# August Plot
august_precip.plot.contourf(ax=axes[1], levels=levels, cmap=cmap, extend='max')
axes[1].set_title('Average Daily Precipitation - August')

# Adjust layout and show the plots
plt.tight_layout()
plt.show()
No description has been provided for this image
  1. Describe the location of the ITCZ in January and August. Without going into the details, explain (in one or two sentences) in January the ictz is further south because that is the area with the highest solar flux and august it moves further north

  2. Describe the precipitation seasonality in West Africa and in India. Name the phenomenon at play. in summer the ictz moves over both west africa and india and brings a strong rainy season called a monsoon season, west-african/ indian monsoon

Part 3: sea-level pressure and surface winds¶

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

1
2
ds = xr.open_dataset('ERA5_LowRes_Monthly_uvslp.nc')
ds
<xarray.Dataset> Size: 1GB
Dimensions:    (longitude: 480, latitude: 241, time: 480)
Coordinates:
  * longitude  (longitude) float32 2kB -179.6 -178.9 -178.1 ... 178.9 179.6
  * latitude   (latitude) float32 964B 90.0 89.25 88.5 ... -88.5 -89.25 -90.0
  * time       (time) datetime64[ns] 4kB 1979-01-01 1979-02-01 ... 2018-12-01
Data variables:
    u10        (time, latitude, longitude) float64 444MB ...
    v10        (time, latitude, longitude) float64 444MB ...
    msl        (time, latitude, longitude) float64 444MB ...
Attributes:
    Conventions:  CF-1.6
    history:      2019-11-24 19:42:05 GMT by grib_to_netcdf-2.14.0: /opt/ecmw...
xarray.Dataset
    • longitude: 480
    • latitude: 241
    • time: 480
    • longitude
      (longitude)
      float32
      -179.6 -178.9 ... 178.9 179.6
      units :
      degrees_east
      long_name :
      longitude
      array([-179.625, -178.875, -178.125, ...,  178.125,  178.875,  179.625],
            dtype=float32)
    • latitude
      (latitude)
      float32
      90.0 89.25 88.5 ... -89.25 -90.0
      units :
      degrees_north
      long_name :
      latitude
      array([ 90.  ,  89.25,  88.5 , ..., -88.5 , -89.25, -90.  ], dtype=float32)
    • time
      (time)
      datetime64[ns]
      1979-01-01 ... 2018-12-01
      long_name :
      time
      array(['1979-01-01T00:00:00.000000000', '1979-02-01T00:00:00.000000000',
             '1979-03-01T00:00:00.000000000', ..., '2018-10-01T00:00:00.000000000',
             '2018-11-01T00:00:00.000000000', '2018-12-01T00:00:00.000000000'],
            dtype='datetime64[ns]')
    • u10
      (time, latitude, longitude)
      float64
      ...
      units :
      m s**-1
      long_name :
      10 metre U wind component
      [55526400 values with dtype=float64]
    • v10
      (time, latitude, longitude)
      float64
      ...
      units :
      m s**-1
      long_name :
      10 metre V wind component
      [55526400 values with dtype=float64]
    • msl
      (time, latitude, longitude)
      float64
      ...
      units :
      Pa
      long_name :
      Mean sea level pressure
      standard_name :
      air_pressure_at_mean_sea_level
      [55526400 values with dtype=float64]
    • longitude
      PandasIndex
      PandasIndex(Index([-179.625, -178.875, -178.125, -177.375, -176.625, -175.875, -175.125,
             -174.375, -173.625, -172.875,
             ...
              172.875,  173.625,  174.375,  175.125,  175.875,  176.625,  177.375,
              178.125,  178.875,  179.625],
            dtype='float32', name='longitude', length=480))
    • latitude
      PandasIndex
      PandasIndex(Index([  90.0,  89.25,   88.5,  87.75,   87.0,  86.25,   85.5,  84.75,   84.0,
              83.25,
             ...
             -83.25,  -84.0, -84.75,  -85.5, -86.25,  -87.0, -87.75,  -88.5, -89.25,
              -90.0],
            dtype='float32', name='latitude', length=241))
    • time
      PandasIndex
      PandasIndex(DatetimeIndex(['1979-01-01', '1979-02-01', '1979-03-01', '1979-04-01',
                     '1979-05-01', '1979-06-01', '1979-07-01', '1979-08-01',
                     '1979-09-01', '1979-10-01',
                     ...
                     '2018-03-01', '2018-04-01', '2018-05-01', '2018-06-01',
                     '2018-07-01', '2018-08-01', '2018-09-01', '2018-10-01',
                     '2018-11-01', '2018-12-01'],
                    dtype='datetime64[ns]', name='time', length=480, freq=None))
  • Conventions :
    CF-1.6
    history :
    2019-11-24 19:42:05 GMT by grib_to_netcdf-2.14.0: /opt/ecmwf/eccodes/bin/grib_to_netcdf -o /cache/data6/adaptor.mars.internal-1574624177.1107812-30733-5-d94977a6-abdd-4375-98f2-61cd60af6f37.nc /cache/tmp/d94977a6-abdd-4375-98f2-61cd60af6f37-adaptor.mars.internal-1574624177.1114278-30733-2-tmp.grib

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:

  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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Your answers here
slp=ds.msl.mean(dim=['time', 'longitude'])*0.01
u10=ds.u10.mean(dim=['time', 'longitude'])
v10=ds.v10.mean(dim=['time', 'longitude'])
atmp=1013.25
zero_line=0

#plots
plt.figure(figsize=(12, 6))
plt.plot(slp['latitude'], slp, label="SLP (hPa)", color='blue')
plt.axhline(y=1013.25, color='red', linestyle='--', label="Standard Atmosphere (1013.25 hPa)")
plt.title("Zonal and Temporal Average of SLP")
plt.xlabel("Latitude")
plt.ylabel("Pressure (hPa)")
plt.legend()
plt.grid()
plt.show()

plt.figure(figsize=(12, 6))
plt.plot(u10['latitude'], u10, label="u10 (m/s)", color='green')
plt.axhline(y=0, color='black', linestyle='--', label="Zero Line")
plt.title("Zonal and Temporal Average of u10")
plt.xlabel("Latitude")
plt.ylabel("Wind Speed (m/s)")
plt.legend()
plt.grid()
plt.show()

plt.figure(figsize=(12, 6))
plt.plot(v10['latitude'], v10, label="v10 (m/s)", color='orange')
plt.axhline(y=0, color='black', linestyle='--', label="Zero Line")
plt.title("Zonal and Temporal Average of v10")
plt.xlabel("Latitude")
plt.ylabel("Wind Speed (m/s)")
plt.legend()
plt.grid()
plt.show()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
  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. it coincides with the location of the circumpolar low, the southern oceanhighs the equatorial trough and the hawaian+bermuda high and the iceland/aleutian low. These climatological highs and lows are connected to the cell model of global circulation. with a large amount of advection at the equator due to solar radiation and a large amount of subsidence at the subtropical high level as well as advection between the ferrell and polar cell.

  2. Similarly, explain the direction and strength of the zonal and meridional winds on earth (tip: the sea-level pressure plot helps)

coincides with subtropical westerlies in both S and N hemispheres as well as with the location of most low pressure systems thus the wind is stronger there. around the itcz the wind speed is 0 in the S-N direction which makes sense as the trade winds meet there. and the eastern trade winds in the tropics NE/SE for S/N hemisphere respectively

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

Download the global average CO$_2$ concentration timeseries data in the CSV format (source: NOAA). Let us help you read them using pandas:

1
2
3
4
5
6
7
df = pd.read_csv('co2_mm_gl.csv', skiprows=38)

# Combine the first two columns into a datetime column and set as index
df['date'] = pd.to_datetime(df.iloc[:, 0].astype(str) + '-' + df.iloc[:, 1].astype(str), format='%Y-%m')
df.set_index('date', inplace=True)

df
year month decimal average average_unc trend trend_unc
date
1979-01-01 1979 1 1979.042 336.56 0.11 335.92 0.10
1979-02-01 1979 2 1979.125 337.29 0.09 336.26 0.10
1979-03-01 1979 3 1979.208 337.88 0.11 336.51 0.10
1979-04-01 1979 4 1979.292 338.32 0.13 336.72 0.11
1979-05-01 1979 5 1979.375 338.26 0.04 336.71 0.11
... ... ... ... ... ... ... ...
2024-06-01 2024 6 2024.458 423.21 0.10 422.64 0.06
2024-07-01 2024 7 2024.542 421.52 0.10 422.83 0.06
2024-08-01 2024 8 2024.625 420.03 0.10 422.90 0.06
2024-09-01 2024 9 2024.708 420.25 0.10 423.09 0.06
2024-10-01 2024 10 2024.792 421.73 0.10 423.25 0.06

550 rows × 7 columns

Prepare three plots:

  • plot the monthly global CO$_2$ concentration as a function of time.
  • plot the annual average timeseries of global CO$_2$ concentration as a function of time.
  • 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).

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. The annual cycle is related to seaonal cycle of vegetation abosrbing CO2 when the pertinent hemisphere is in summer through photosynthesis and releasing it when it is in winter through decay.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Required imports
import pandas as pd

# Load the CO2 dataset, skipping the first 38 rows as metadata
df = pd.read_csv('co2_mm_gl.csv', skiprows=38)

# Combine the 'year' and 'month' columns into a datetime column and set it as the index
df['date'] = pd.to_datetime(df.iloc[:, 0].astype(str) + '-' + df.iloc[:, 1].astype(str), format='%Y-%m')
df.set_index('date', inplace=True)

# Display the first few rows to verify the data
print(df.head())

# Use the 'average' column for CO2 concentrations
# Resample to annual means
co2_annual = df['average'].resample('YE').mean()

# Extract relevant periods: 1980-1985 and 2016-2021
co2_1980_1985 = co2_annual.loc['1980':'1985']
co2_2016_2021 = co2_annual.loc['2016':'2021']

# Compute annual increase for both periods
annual_increase_1980_1985 = (co2_1980_1985.iloc[-1] - co2_1980_1985.iloc[0]) / len(co2_1980_1985)
annual_increase_2016_2021 = (co2_2016_2021.iloc[-1] - co2_2016_2021.iloc[0]) / len(co2_2016_2021)

# Print results
print("Pre-industrial CO2 concentration: ~280 ppm")
print(f"Annual increase in CO2 concentration (1980-1985): {annual_increase_1980_1985:.2f} ppm/year")
print(f"Annual increase in CO2 concentration (2016-2021): {annual_increase_2016_2021:.2f} ppm/year")
            year  month   decimal  average  average_unc   trend  trend_unc
date                                                                      
1979-01-01  1979      1  1979.042   336.56         0.11  335.92       0.10
1979-02-01  1979      2  1979.125   337.29         0.09  336.26       0.10
1979-03-01  1979      3  1979.208   337.88         0.11  336.51       0.10
1979-04-01  1979      4  1979.292   338.32         0.13  336.72       0.11
1979-05-01  1979      5  1979.375   338.26         0.04  336.71       0.11
Pre-industrial CO2 concentration: ~280 ppm
Annual increase in CO2 concentration (1980-1985): 1.10 ppm/year
Annual increase in CO2 concentration (2016-2021): 1.94 ppm/year
  1. 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. Global temperatures closely track rising CO levels because CO traps heat in the atmosphere, acting like a thermal blanket and driving the greenhouse effect. The data clearly shows both increasing over time. However, other factors also influence temperature changes, such as variations in solar energy during cycles like the Sun’s 11-year cycle, volcanic eruptions that release particles to cool the planet by reflecting sunlight, and land use changes like deforestation, which alter how much heat the Earth absorbs or reflects. These factors combine with CO to shape global temperature trends.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# Your answers here
df.average.plot()

co_data=df

co2_A=co_data.resample('YE').mean()

ds = xr.open_dataset('ERA5_LowRes_Monthly_t2m.nc')

temp_global_annual = ds['t2m'].mean(dim=['latitude', 'longitude']).resample(time='Y').mean()

# Convert temperature xarray to pandas DataFrame for alignment
temp_global_annual_df = temp_global_annual.to_dataframe().reset_index()
temp_global_annual_df.set_index('time', inplace=True)

# Align both datasets to the same time range
common_range = co2_A.index.intersection(temp_global_annual_df.index)
co2_A = co2_A.loc[common_range]
temp_global_annual_df = temp_global_annual_df.loc[common_range]

# Plot CO2 concentration and temperature on the same graph
fig, ax1 = plt.subplots(figsize=(12, 6))

# Plot CO2 concentration
ax1.plot(co2_A.index.year, co2_A['average'], color='blue', label='Global CO2 Concentration')
ax1.set_xlabel("Year")
ax1.set_ylabel("CO2 Concentration (ppm)", color='blue')
ax1.tick_params(axis='y', labelcolor='blue')
ax1.grid()

# Add 2m temperature on a secondary y-axis
ax2 = ax1.twinx()
ax2.plot(temp_global_annual_df.index.year, temp_global_annual_df['t2m'], color='red', label='Global 2m Temperature')
ax2.set_ylabel("Temperature (°C)", color='red')
ax2.tick_params(axis='y', labelcolor='red')

# Add legends and title
fig.suptitle("Annual Average Time Series of Global CO2 Concentration and 2m Temperature")
fig.legend(loc="upper left", bbox_to_anchor=(0.1, 0.9))
plt.show()
/Users/sunshine/mambaforge/envs/inpro/lib/python3.12/site-packages/xarray/groupers.py:403: FutureWarning: 'Y' is deprecated and will be removed in a future version, please use 'YE' instead.
  self.index_grouper = pd.Grouper(
No description has been provided for this image
No description has been provided for this image

Part 5: Climate Risk Dashboard (open research question)¶

We will use the Climate Risk Dashboard climate-risk-dashboard.climateanalytics.org from the Horizon 2020 PROVIDE Project to create and analyze an open research question. To learn how to use the dashboard, visit the OGGM-Edu website, which includes examples using mean temperature and glacier volume.

  1. Select Indicators and Geography

    • Choose two to three Indicator's from the dashboard. Ensure that you pick only one Indicator per Sector (e.g., one from Terrestrial Climate and one from Biodiversity).
    • Select a GEOGRAPHY for your Indicator's (not all geographies are available for all indicators).
      • Try to pick related locations. For example, if you choose a city, also consider selecting its country or region for comparison.
      • Or, if it fits to your research question, you can also select an additional GEOGRAPHY for comparison (e.g. compare two countries), but you do not have to.
  2. Formulate a Research Question

    • Based on your selected Indicator's and GEOGRAPHY, create a research question.
      • Please mention this research question clearly in your notebook.
    • Your analysis will focus on comparing two to three scenarios available in the dashboard.
  3. Conduct the Analysis

    • Visualizations:
      • Use at least one plot per Indicator by downloading plots directly from the dashboard. You can add them to your notebook with drag-and-drop.
      • Further, include at least two custom plots in your analysis. This means download the raw data from the dashboard and create your own plot. For an example see this section on OGGM-Edu. Please use the original filenames of the downloaded data in your notebook.
    • References:
      • Try to find at least one reference (reports, papers or articls) related to your research question and mention them in the notebook by providing a link. A good resource for many climate change related topics is the IPCC report.
  4. Answer Guiding Questions to your Research Question

    • Answer at least three of the questions below, or come up with your own creative questions! You’re encouraged to mix and match the provided questions with your own ideas or explore different angles that interest you.

      • How do the scenarios differ over time for each Indicator?
      • What are the spatial differences between scenarios for each Indicator?
      • If applicable: How much risk is avoided by staying below a certain threshold for your Indicator?
      • Are there any correlations between your selected Indicators?

Research Question: How do projected changes in mean temperature and glacier volume in Austria under SSP1-1.9 and SSP5-3.4-OS scenarios impact the viability of summer skiing on Austrian glaciers by 2100?

Reference Paper: https://link.springer.com/article/10.1007/s00484-022-02371-6

Developement of Summer Skiing Days in Austrian Glacier Ski Areas in the First Two Decades of The Twenty-First Century

Under both SSP1-1.9 (low emissions) and SSP5-3.4-OS (intermediate emissions), temperatures in Austria are projected to stabilize by the 2060s. However, glaciers continue to retreat well beyond this point, with significant losses persisting into the 2080s and 2090s. This lag between temperature stabilization and glacier response reflects the slow dynamics of ice melt and accumulated thermal energy. By 2100, even under the SSP1-1.9 scenario, most Austrian glaciers will have disappeared, highlighting the inevitability of glacier loss due to past and ongoing warming. Switzerland shows nearly identical trends, indicating that climate impacts on glaciers are consistent across the Alpine region, regardless of national boundaries.

The correlation between rising mean temperatures and glacier retreat is clear, but the delayed response of glaciers to warming amplifies the long-term impacts of climate change. Even stabilizing temperatures at 1.5°C (SSP1-1.9) does little to prevent significant glacier loss. According to the referenced paper, modern glaciers are already too small to sustain summer skiing in Austria, and this trend will only worsen. The paper highlights that the viability of summer skiing has declined dramatically in the past two decades, and projections confirm that by 2100, no glaciers will remain large enough to support skiing under any emission scenario.

These findings emphasize not only the loss of recreational opportunities like summer skiing but also the broader implications for Alpine tourism, water resources, and local ecosystems. While SSP1-1.9 avoids some glacier loss compared to SSP5-3.4-OS, the differences are minimal, and the ultimate outcome is the near-total disappearance of glaciers in the region. The data and projections underscore the reality that, for Alpine glaciers, the damage is already largely done, and their disappearance is inevitable even with significant global climate action.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import pandas as pd
import matplotlib.pyplot as plt

# Read the CSV files into DataFrames
aut_df = pd.read_csv('impact-time_AUT_curpol_glacier-volume_0.5_present-day-2020.csv')
che_df = pd.read_csv('impact-time_CHE_curpol_glacier-volume_0.5_present-day-2020.csv')

# Plot for Austria
plt.figure(figsize=(10, 6))
plt.plot(aut_df['year'], aut_df['glacier-volume_min'], label='Min Glacier Volume (AUT)', color='lightblue')
plt.plot(aut_df['year'], aut_df['glacier-volume_mean'], label='Mean Glacier Volume (AUT)', color='blue')
plt.plot(aut_df['year'], aut_df['glacier-volume_max'], label='Max Glacier Volume (AUT)', color='darkblue')
plt.title('Projected Glacier Volume Decline in Austria (2020–2100)')
plt.xlabel('Year')
plt.ylabel('Relative Glacier Volume (Normalized to 1 in 2020)')
plt.legend()
plt.grid(True)
plt.show()

# Plot for Switzerland
plt.figure(figsize=(10, 6))
plt.plot(che_df['year'], che_df['glacier-volume_min'], label='Min Glacier Volume (CHE)', color='lightgreen')
plt.plot(che_df['year'], che_df['glacier-volume_mean'], label='Mean Glacier Volume (CHE)', color='green')
plt.plot(che_df['year'], che_df['glacier-volume_max'], label='Max Glacier Volume (CHE)', color='darkgreen')
plt.title('Projected Glacier Volume Decline in Switzerland (2020–2100)')
plt.xlabel('Year')
plt.ylabel('Relative Glacier Volume (Normalized to 1 in 2020)')
plt.legend()
plt.grid(True)
plt.show()
No description has been provided for this image
No description has been provided for this image

aut-glacier_volume-present_day_2020-area-annual-0_02-ssp_119_ssp_534_over.png

che-glacier_volume-present_day_2020-area-annual-0_02-ssp_119_ssp_534_over.png

aut-terclim_mean_temperature-annual-present_day-area-0_02-ssp_119_ssp_534_over.png

che-terclim_mean_temperature-annual-present_day-area-0_02-ssp_119_ssp_534_over.png

1