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 12 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 my 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. I 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
I 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 10 11 12 13 | # Import the tools we are going to need today: import matplotlib.pyplot as plt # plotting library import matplotlib.gridspec as gridspec # extension for mathplotlib (to megre plots) 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.feature as cfeature # extension tool for cartopy import cartopy.crs as ccrs # Projections list import proplot as plot # Geospacial plots # Some defaults: plt.rcParams['figure.figsize'] = (12, 5) # Default plot size |
NOTE:¶
There might be some issues with the used colors in the plots. This might be, because the version on which this notebook was written, isn't the newest one, therefore the syntax for some colors varies a little bit.
However, all plots are visible in the html document of this project.
Part 1 - temperature climatology¶
Open the ERA5 temperature data:
1 2 3 4 5 6 | # import data ERA5 'low resolution of monthly air temperature @ 2m' ds_ERA5_T2m = xr.open_dataset('C:/Users/Mazzurana/Desktop/Uni_Atmosphaerenwissenschaften/Semester_5/Climate_Systems/Python/Data/ERA5_LowRes_Monthly_t2m.nc') # check dataset ds_ERA5_T2m #ds_ERA5_T2m.info() |
<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:
- 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 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.
Questions:
- Look at the zonal temperature anomaly map.
- Explain why norther 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.
- 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.
Answers for No. 1¶
(Maps below) Nothern Europe and the North Atlantic Region are under the influence of the Golf Stream which carries (Sub)-Tropical warmth over the Ocean to the north. Since the Golf Stream is due to deep ocean circulation and the positioning of the continents how they are, a such stron energy-conveyer belt cant form in the Pacific (Thermohaline Circulation)
Answers for No. 2¶
(Maps below) The temperature range is way higher in higher latitudes, beacuase there are cold (low temperatures) and warm seasons (high temperatures). As a comparison, in the temperature differences in the Tropics between the seasonal changes (rain- and dry-periods) are not that high, therefore the total range is not that big.
Water in gerneral (here specifically oceans) have a higher heating capacity than land masses. Furthermore, oceans store energy (warmth) way better then land masses. Therefore they dont cool down as much as land masses during cold seasons, and they are not steady, circulation is happening throughout the whole year. So temperatures stay approximately constant in the oceans.
The highest anomalies can be seen in the regions with continental climate (Siberia and Nothern America)
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 | # define mean temperature @ 2 m (T_mean) and mean temperature @ 2 m in °C (Tc2m_mean) T_mean = ds_ERA5_T2m.t2m.mean(dim='time') Tc2m_mean = T_mean - 273.15 # fig(1): computed mean temperature on a global scale over the entire dataset period (1979-2019) converted to °C ax = plt.subplot(2, 2, 1, projection = ccrs.Robinson()) Tc2m_mean.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'bwr', cbar_kwargs = {'label':'Temperature @ 2 m [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--', draw_labels = True); ax.ylabels_right = ax.xlabels_top = False; plt.title('(A) Mean Temperature [°C] without levels'); # fig(2): plotted mean temperature on a global scale, 12 levels, legend and (color) theme ax = plt.subplot(2, 2, 2, projection = ccrs.Robinson()) Tc2m_mean.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'bwr', levels =15, cbar_kwargs = {'label':'Temperature @ 2 m [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--', draw_labels = True); ax.ylabels_right = ax.xlabels_top = False; plt.title('(B) Mean Temperature [°C] with levels (for better visualisation)'); # fig(3): global mean temperature for Latitudes over the entire dataset period (1979-2019) in °C ax = plt.subplot(2, 2, 3) Tc2m_mean.mean(dim = 'longitude').plot(label = 'Mean Temperature [°C]') plt.title('(C) Global Mean Temperature [°C] over Latitudes. Note that zonal anomalies can`t be seen clearly') plt.xlim(-90, 90) plt.grid(True) # with .axvspan(x1,x2, [properties]) we defined the colored bars to ease the visualization of the different climate zones plt.axvspan(-90, -66.33, color = 'blue', alpha = 0.2, label = 'Polar Region') plt.axvspan(90, 66.33, color = 'blue', alpha = 0.2) plt.axvspan(-66.33, -40, color = 'light blue', alpha = 0.2, label = 'Mid-Latitudes') plt.axvspan(66.33, 40, color = 'light blue', alpha = 0.2) plt.axvspan(-40, -23.5, color = 'light red', alpha = 0.2, label = 'Sub-Tropics') plt.axvspan(40, 23.5, color = 'light red', alpha = 0.2) plt.axvspan(-23.5, 23.5, color = 'red', alpha = 0.2, label = 'Tropics') plt.axhline(y = 0, color = 'black', alpha =0.5, linestyle = '--', label = '0°C') # horizontal reference line plt.legend(loc='best') plt.ylabel('Mean Temperature @ 2 m [°C]') plt.xlabel('Latitude [deg]'); |
1 2 3 4 5 6 7 8 | # zonal anomalies in °C Tc2m_mean_an = Tc2m_mean - Tc2m_mean.mean(dim='longitude') #Tc_mean_an = T_mean_an - 273.15 # plotted on a map as above: ax = plt.axes(projection=ccrs.Robinson()) Tc2m_mean_an.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'bwr', levels =15, cbar_kwargs = {'label':'Zonal Temperature Anomaly @ 2 m [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', draw_labels = True, linestyle = '--'); ax.ylabels_right = ax.xlabels_top = False; plt.title('Zonal Temperature Anomaly [°C]'); |
1 2 3 4 5 6 7 8 9 10 11 12 | # mothly average temperature (range) and plotted # first we have to define the monthly average temperature and convert if to °C for better visualisation later T2m_month_av = ds_ERA5_T2m.groupby('time.month').mean() Tc2m_month_av = T2m_month_av - 273.15 # converted to °C Tc2m_month_av Tc2m_month_av_range = Tc2m_month_av.t2m.max(dim = 'month') - Tc2m_month_av.t2m.min(dim = 'month') Tc2m_month_av_range # plot ax = plt.axes(projection=ccrs.Robinson()) Tc2m_month_av_range.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'bwr', levels =15, cbar_kwargs = {'label':'Monthly Temperature Range @ 2 m [°C]'}) ax.coastlines(); ax.gridlines(draw_labels = True, linestyle = '--', alpha = 0.5); ax.ylabels_right = ax.xlabels_top = False; plt.title('Temperature Range (monthly) [°C]'); |
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 3 4 | ds_ERA5_tp = xr.open_dataset('C:/Users/Mazzurana/Desktop/Uni_Atmosphaerenwissenschaften/Semester_5/Climate_Systems/Python/Data/ERA5_LowRes_Monthly_tp.nc') # explore the dataset ds_ERA5_tp #ds_ERA5_tp.info() |
<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: tp (time, latitude, longitude) float32 ... Attributes: Conventions: CF-1.6 history: 2019-11-18 09:30:18 GMT by grib_to_netcdf-2.14.0: /opt/ecmw...
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:
- Describe the location of the ITCZ in January and February. Without going into the details, explain (in one or two sentences)
- Describe the precipitation seasonality in West Africa and in India. Name the phenomenon at play.
Answer 1¶
By January and February the ITCZ (Intertropical Convergence Zone) is located over South America, Central Africa and Australia. The ICTZ is strongly linked to the suns radiation with a dilay of 1-2 months. While in the months closer to the nothern-hemispherical summer the ITCZ is located on the nothern part of the equator, in the months closest to the start of the southern-hemispherical summer, it directs to the southern part of the equator (the ITCZ follows the sun)
Answer 2¶
(maps below)
The strong precipitation happening in West Africa and India are due to oscillation of the ITCZ. Both precipitation mixima are called monsoon.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # plots and more tp_d_average = ds_ERA5_tp.groupby('time.month').mean(dim = 'time') tp_d_average_jan = tp_d_average.sel(month = 1) # January mean 1979-2019 tp_d_average_mm_jan = tp_d_average_jan.tp * 1000 #convert to mm/d tp_d_average_aug = tp_d_average.sel(month = 8) # August mean 1979-2019 tp_d_average_mm_aug = tp_d_average_aug.tp * 1000 #convert to mm/d # fig(1): monthly pecipitation mean (January, 1979-2019) converted to mm/d ax = plt.subplot(1, 2, 1, projection = ccrs.Robinson()) tp_d_average_mm_jan.plot(ax = ax, transform = ccrs.PlateCarree(), cmap = 'ylgnbu', levels =[0.5, 1, 2, 3, 4, 5, 7, 10, 15, 20, 40], cbar_kwargs = {'label':'Monthly Precipitation Mean (January, 1979-2019) [mm/d]'}) ax.coastlines(); ax.gridlines(draw_labels = True, linestyle = '--', alpha = 0.5); ax.ylabels_right = ax.xlabels_top = False; plt.title('(A) Precipitation Mean in January (1979-2019) [mm/d]'); # fig(2): monthly pecipitation mean (December, 1979-2019) converted to mm/d ax = plt.subplot(1, 2, 2, projection = ccrs.Robinson()) tp_d_average_mm_aug.plot(ax = ax, transform = ccrs.PlateCarree(), cmap = 'ylgnbu', levels =[0.5, 1, 2, 3, 4, 5, 7, 10, 15, 20, 40], cbar_kwargs = {'label':'Monthly Precipitation Mean (August, 1979-2019) [mm/d]'}) ax.coastlines(); ax.gridlines(draw_labels = True, linestyle = '--', alpha = 0.5); ax.ylabels_right = ax.xlabels_top = False; plt.title('(B) Precipitation Mean in August (1979-2019) [mm/d]'); |
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 3 | # analogue to the past few sections, the corresponding dataset was imported and explored ds_ERA5_uvslp = xr.open_dataset('C:/Users/Mazzurana/Desktop/Uni_Atmosphaerenwissenschaften/Semester_5/Climate_Systems/Python/Data/ERA5_LowRes_Monthly_uvslp.nc') ds_ERA5_uvslp |
<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: u10 (time, latitude, longitude) float32 ... v10 (time, latitude, longitude) float32 ... msl (time, latitude, longitude) float32 ... Attributes: Conventions: CF-1.6 history: 2019-11-24 19:42:05 GMT by grib_to_netcdf-2.14.0: /opt/ecmw...
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:
- 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.
- Similarly, explain the direction and strength of the zonal and meridional winds on earth (tip: the sea-level pressure plot helps)
Answer No. 1¶
Low pressure systems mostly develop over strongly heated areas; warm air tends to rise and therefore creats a low pressure system, while cold regions are characterized by being a development field for high pressure systems (cold air sinks). This effect creates strong winds: since the atmosphere strives for equilibrium, air gets "sucked" into the bottom of low-pressure systems. High-pressure systems have the opposite effect; they "push" air out. The position of such pressure system areas on Earth and their influence on wind-development create different cells in the Earths surface. Those cells can be imagined as dividers in the atmosphere. Singular pressure systems can't (hardly can) overcome such a cellborder. This is why we can see an accumulation of low-pressure systems between certain logitudes and high-pressure systems in others.
Latitude [deg] | Characteristic Pressure Systems | Circulation System | Wind Direction of the Cells |
---|---|---|---|
Equator, 0° | Low-Pressure Systems (Intertropical Convergence Zone) | / | / |
Sub-Tropics | / | Hardley Cell(s) | Northeast-, Southeast (Trade Winds) |
$\approx\pm$ 30° | High-Pressure Systems (Horse Latitudes) | / | / |
Mid-Latitudes | / | Ferell Cell(s) | Westwinds |
$\approx\pm$ 66° | Polar Low-Pressure Systems | / | / |
Polar Zones | Polar High | Polar Cell(s) | Polar Eastwinds |
Answer No. 2¶
As already in Answer No. 1 shortly described, winds can be the result of low- and high-pressure systems. The winds in the Ferell-Cell on the southern hemisphere are very strong, because of missing surface roughness (mostly ocean). The winds in the Ferell-Cell on the nothern hemisphere get deviated and slowed down by the topography of the underlying continents. Therefore the winds, as seen in the last plot of this section are significantly weaker than their equivalents on the southern hemisphere.
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 | # plots and more (mean sea level pressure) ds_ERA5_msl_average = ds_ERA5_uvslp.msl.mean(dim = 'time') ds_ERA5_msl_average_hPa = ds_ERA5_msl_average / 100 # convert from Pa to hPa # plotting fun ds_ERA5_msl_average_hPa.mean(dim = 'longitude').plot(label = 'Mean Sea Level Pressure over Latitudes [hPa]') plt.title('Global Mean Sea Level Pressure [hPa] over Latitudes') plt.xlim(-90, 90) plt.grid(True) # with .axvspan(x1,x2, [properties]) we defined the colored bars to ease the visualization of the different climate zones plt.axvspan(-90, -66.33, color = 'blue', alpha = 0.2, label = 'Polar Region') plt.axvspan(90, 66.33, color = 'blue', alpha = 0.2) plt.axvspan(-66.33, -40, color = 'light blue', alpha = 0.2, label = 'Mid-Latitudes') plt.axvspan(66.33, 40, color = 'light blue', alpha = 0.2) plt.axvspan(-40, -23.5, color = 'light red', alpha = 0.2, label = 'Sub-Tropics') plt.axvspan(40, 23.5, color = 'light red', alpha = 0.2) plt.axvspan(-23.5, 23.5, color = 'red', alpha = 0.2, label = 'Tropics') # with .annotate('label',xy = (x_arrowtip,y_arrowtip),xytext=(x_textbottomleft,y_textbottomleft), [properties]) the arrows have # been addedt to the plot to mark some local maxima and minima with the right description. # note that the coordinates of the condition yxtext=(x,y) arent coherent when pointing to a text (two arrows -> one text). # this is because otherwise the arrows would cut through the text. plt.annotate('Polar Low-Pressure Systems', xy=(-60, 985), xytext=(-10, 990), arrowprops=dict(arrowstyle = "->", facecolor='black', connectionstyle = "arc3")) plt.annotate('', xy=(60, 1011), xytext=(0, 991), arrowprops=dict(arrowstyle = "->", facecolor='black', connectionstyle = "arc3")) plt.annotate('(Innertropical Convergence Zone)', xy=(0, 1011), xytext=(-10, 1016), arrowprops=dict(arrowstyle = "->", facecolor='black', connectionstyle = "arc3")) plt.annotate('Horse Latitudes', xy=(-32.5, 1017), xytext=(-10, 995), arrowprops=dict(arrowstyle = "->", facecolor='black', connectionstyle = "arc3")) plt.annotate('', xy=(35, 1016), xytext=(0, 996), arrowprops=dict(arrowstyle = "->", facecolor='black', connectionstyle = "arc3")) # reference line(s) # the 'h' in ax_line stands for horizontal while the 'v' stands for vertical. plt.axhline(y = 1013.25, color = 'black', linestyle = '--', label = 'Mean Sea Level Pressure (1013.25 hPa)') plt.axvline(x = -66.33 , color = 'orange', label = 'Polar Circle') plt.axvline(x = 66.33 , color = 'orange') plt.legend(loc='lower right') plt.ylabel('Mean Pressure @ Sea Level [hPa]') plt.xlabel('Latitude [deg]'); |
1 2 3 4 5 6 7 8 | # here just a more colorful plot to really highlight the in Answer no. 1 described pressure regions # this was done 'quick´n´dirty', just to have a better overview. ds_ERA5_msl_average_hPa.plot.contourf(levels=20); plt.axhline(y = 0, color = 'black', alpha = 0.3, label = 'Equator'); plt.axhline(y = -66.33, color = 'red', alpha = 0.4, label = 'Polar Circle'); plt.axhline(y = 66.33, color = 'red', alpha = 0.4); plt.legend(loc = 'best'); plt.title('Colorful Visualizatio of the Pressure Distribution'), |
(Text(0.5, 1.0, 'Colorful Visualizatio of the Pressure Distribution'),)
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 | # plots and variables (u10) ds_ERA5_u10_average = ds_ERA5_uvslp.u10.mean(dim = 'time') # plotting fun (u10) ds_ERA5_u10_average.mean(dim = 'longitude').plot(label = 'Mean Wind Velocity, u component [$m\ s^{-1}$]') plt.title('Mean Wind Velocity, u component [$m\ s^{-1}$] over Latitudes') plt.xlim(-90, 90) plt.grid(True) # with .axvspan(x1,x2, [properties]) we defined the colored bars to ease the visualization of the different climate zones plt.axvspan(-90, -66.33, color = 'blue', alpha = 0.2, label = 'Polar Region') plt.axvspan(90, 66.33, color = 'blue', alpha = 0.2) plt.axvspan(-66.33, -40, color = 'light blue', alpha = 0.2, label = 'Mid-Latitudes') plt.axvspan(66.33, 40, color = 'light blue', alpha = 0.2) plt.axvspan(-40, -23.5, color = 'light red', alpha = 0.2, label = 'Sub-Tropics') plt.axvspan(40, 23.5, color = 'light red', alpha = 0.2) plt.axvspan(-23.5, 23.5, color = 'red', alpha = 0.2, label = 'Tropics') # reference line(s) # the 'h' in ax_line stands for horizontal while the 'v' stands for vertical. plt.axhline(y = 0, color = 'black', linestyle = '--', label = 'No Wind') plt.axvline(x = -66.33 , color = 'orange', linestyle = '--', label = 'Polar Circle') plt.axvline(x = 66.33 , color = 'orange', linestyle = '--') plt.legend(loc='best') plt.ylabel('Mean Wind Velocity, u component [$m\ s^{-1}$]') plt.xlabel('Latitude [deg]'); |
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 | # plots and variables (v10) ds_ERA5_v10_average = ds_ERA5_uvslp.v10.mean(dim = 'time') # plotting fun 2 (v10) ds_ERA5_v10_average.mean(dim = 'longitude').plot(label = 'Mean Wind Velocity, v component [$m\ s^{-1}$]') plt.title('Mean Wind Velocity, u and v components [$m\ s^{-1}$] over Latitudes') plt.xlim(-90, 90) plt.grid(True) # with .axvspan(x1,x2, [properties]) we defined the colored bars to ease the visualization of the different climate zones plt.axvspan(-90, -66.33, color = 'blue', alpha = 0.2, label = 'Polar Region') plt.axvspan(90, 66.33, color = 'blue', alpha = 0.2) plt.axvspan(-66.33, -40, color = 'light blue', alpha = 0.2, label = 'Mid-Latitudes') plt.axvspan(66.33, 40, color = 'light blue', alpha = 0.2) plt.axvspan(-40, -23.5, color = 'light red', alpha = 0.2, label = 'Sub-Tropics') plt.axvspan(40, 23.5, color = 'light red', alpha = 0.2) plt.axvspan(-23.5, 23.5, color = 'red', alpha = 0.2, label = 'Tropics') # reference line(s) # the 'h' in ax_line stands for horizontal while the 'v' stands for vertical. plt.axhline(y = 0, color = 'black', linestyle = '--', label = 'No Wind') # reference line plt.axvline(x = -66.33 , color = 'orange', linestyle = '--', label = 'Polar Circle') plt.axvline(x = 66.33 , color = 'orange', linestyle = '--') plt.legend(loc = 'upper right') plt.ylabel('Mean Wind Velocity [$m\ s^{-1}$]') plt.xlabel('Latitude [deg]'); |
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 | # combined u- and v-component ds_ERA5_u10_average.mean(dim = 'longitude').plot(color = 'blue', label = 'Mean Wind Velocity, u component [$m\ s^{-1}$]') ds_ERA5_v10_average.mean(dim = 'longitude').plot(color = 'red', label = 'Mean Wind Velocity, v component [$m\ s^{-1}$]') plt.title('Mean Wind Velocity, u and v components [$m\ s^{-1}$] over Latitudes') plt.xlim(-90, 90) plt.grid(True) # with .axvspan(x1,x2, [properties]) we defined the colored bars to ease the visualization of the different climate zones plt.axvspan(-90, -66.33, color = 'blue', alpha = 0.2, label = 'Polar Region') plt.axvspan(90, 66.33, color = 'blue', alpha = 0.2) plt.axvspan(-66.33, -40, color = 'light blue', alpha = 0.2, label = 'Mid-Latitudes') plt.axvspan(66.33, 40, color = 'light blue', alpha = 0.2) plt.axvspan(-40, -23.5, color = 'light red', alpha = 0.2, label = 'Sub-Tropics') plt.axvspan(40, 23.5, color = 'light red', alpha = 0.2) plt.axvspan(-23.5, 23.5, color = 'red', alpha = 0.2, label = 'Tropics') # reference line(s) # the 'h' in ax_line stands for horizontal while the 'v' stands for vertical. plt.axhline(y = 0, color = 'black', linestyle = '--', label = 'No Wind') plt.axvline(x = -66.33 , color = 'orange', linestyle = '--', label = 'Polar Circle') plt.axvline(x = 66.33 , color = 'orange', linestyle = '--') plt.legend(loc = 'upper right') plt.ylabel('Mean Wind Velocity [$m\ s^{-1}$]') plt.xlabel('Latitude [deg]'); |
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:
1 2 3 4 | # analogue to the past few sections, the corresponding data was imported and explored. The only difference here was the type of data. # In this example, the data was in form of a dataframe. df_co2_mm_gl = pd.read_csv('C:/Users/Mazzurana/Desktop/Uni_Atmosphaerenwissenschaften/Semester_5/Climate_Systems/Python/Data/co2_mm_gl.csv', skiprows=55, parse_dates={'date' : [0, 1]}, index_col='date') df_co2_mm_gl |
decimal | average | average_unc | trend | trend_unc | |
---|---|---|---|---|---|
date | |||||
1979-01-01 | 1979.042 | 336.56 | 0.10 | 335.92 | 0.09 |
1979-02-01 | 1979.125 | 337.29 | 0.09 | 336.25 | 0.09 |
1979-03-01 | 1979.208 | 337.88 | 0.10 | 336.51 | 0.09 |
1979-04-01 | 1979.292 | 338.32 | 0.11 | 336.72 | 0.09 |
1979-05-01 | 1979.375 | 338.26 | 0.04 | 336.71 | 0.10 |
... | ... | ... | ... | ... | ... |
2022-05-01 | 2022.375 | 418.43 | 0.10 | 416.79 | 0.06 |
2022-06-01 | 2022.458 | 417.48 | 0.10 | 416.89 | 0.06 |
2022-07-01 | 2022.542 | 415.72 | 0.10 | 417.01 | 0.06 |
2022-08-01 | 2022.625 | 414.38 | 0.10 | 417.22 | 0.06 |
2022-09-01 | 2022.708 | 414.57 | 0.10 | 417.40 | 0.06 |
525 rows × 5 columns
Prepare three plots: plot the annual average timeseries of global CO2 concentration and of global 2m temperature from ERA5 on the same plot (using a secondary y axis for temperature)
- 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:
- Describe and explain the annual cycle of CO$_2$ concentrations
- 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.
- 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.
Answer No. 1¶
The annual CO$_2$ Concentration(not respecting the gradual rise of the concentration in Plot 1) oscillates between high and low values. This comes from the seasonal changes on earth. During the time of the nothern-hemispherical winter the Co$_2$ concentration rises to its maxima. That is, because decaing organic material (eg. plants, leaves ect.) release a huge amount of CO$_2$ into the atmosphere while the new-growing ones in summer (nothern hemispere) take up that CO$_2$ again. The curve looks like this, because most of the landmasses are distributed on the nothern hemisphere, therefore there is more space for such organisms to grow in the warm seasons.
Answer No. 2¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ## Calculations for Answer No. 2: # assign the located interval to a new variable (here 1980-1985) co2_1980_1985 = df_co2_mm_gl.loc['1980':'1985'] # analogue as above but for 2016-2021 co2_2016_2021 = df_co2_mm_gl.loc['2016':'2021'] # now calculate the linear fit in the period 1980-1985 and 2016-2021 and compare the slopes #first i will convert to xarrays again to group the means by years ds_co2_80_85 = co2_1980_1985.to_xarray() ds_co2_16_21 = co2_2016_2021.to_xarray() #group by year and calculate the mean of the average to get 5 values each co2_mean_80_85 = ds_co2_80_85.groupby('date.year').mean() co2_mean_16_21 = ds_co2_16_21.groupby('date.year').mean() # now use polyfit to calculate the slope of the linear fit x1 = [1,2,3,4,5,6] y1 = co2_mean_80_85.average regression_80_85 = np.polyfit(x1,y1,1) # analogue to above now for 2016-2021 y2 = co2_mean_16_21.average regression_16_21 = np.polyfit(x1,y2,1) |
We get two arrays containing two elements as a result: first element in the array is the a-value of a linear function, the second value is the b-value of the function a gives the slope of the linear regression: we get a slope of $\approx$ 1.334 for the period 1980-1985 and $\approx$ 2.354 for the period 2016-2021. This is an equivalent of an increase of 1.33 ppm/year in the years between 1980-1985 and an increase of 2.35 ppm/year in the years 2016-2021.
$[$Input$]$: regression_80_85
$[$Output$]$: array($[ 1.33407143, 337.33394444]$)
$[$Input$]$: regression_16_21
$[$Output$]$: array($[ 2.35388095, 400.61544444]$)
Answer No. 3¶
Rising CO$_2$ concentration influences the temperature. The more CO$_2$ the higher the temperature. Other effects on the global mean Temperature are large scale volcano eruptions (eg. Pinatubo). The erupted soot can cause an increased cloud formation which can flatten the temperature curve (less sw-radiation incoming). Furthermore can the solar cycle influence global temperature. An so called internal driver of climate change could be any form of ocean current change.
1 2 3 4 5 6 7 | # plot the monthly global CO2 concentration as a function of time. df_co2_mm_gl.average.plot(label = 'CO$_2$-Concentration [ppm]'); plt.title('Monthly CO$_2$-Concentration [ppm] from 1979-2022') plt.grid(True) plt.legend(loc = 'lower right') plt.ylabel('CO$_2$-Concentration [ppm]') plt.xlabel('Time [years]'); |
1 2 3 4 5 6 7 8 9 10 11 12 | # plot the annual average timeseries of global CO2 concentration as a function of time. # to handle the data in a more efficient way, it was converted from a Pandas Dataframe to a xarray Dataset ds_co2_mm_gl = df_co2_mm_gl.to_xarray() # group the dataset over the years and calculate the mean over it. then plot it ds_co2_mm_gl_yeas = ds_co2_mm_gl.groupby('date.year').mean() ds_co2_mm_gl_yeas.average.plot(label = 'CO$_2$-Concentration [ppm]') plt.title('Annual CO$_2$-Concentration [ppm] from 1979-2022') plt.xlim(1979,2022) plt.grid(True) plt.legend(loc = 'lower right') plt.ylabel('CO$_2$-Concentration [ppm]') plt.xlabel('Time [years]'); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # plot the annual average timeseries of global CO2 concentration and of global 2m temperature from ERA5 on the same plot # (using a secondary y axis for temperature) # since the earth is a sphere, the temperature must bei weighted properly to avoid an error because of the poles # being "too large". In other words, we estimate for the geographic distribution of temperature and then take # an area-weighted average at each time interval in the series. # first we calculate a weighted function to multiply our temperature array with, to give the poles less # weight in the calculation. Thats why we use the cosine. Its properties is to go to zero when approaching the poles (\pm 90 °C) weight = np.cos(np.deg2rad(ds_ERA5_T2m.latitude)) weight = weight / weight.sum() ds_T2_weighted = ds_ERA5_T2m.t2m.mean(dim = 'longitude') * weight T2_w_latlon = ds_T2_weighted.sum(dim = 'latitude') -273.15 # convert into °C # now we have calculated each "slice" of the existing data-cube (1 slice equals 1 month of the 40-year long measurements) # we continue by reducing the 12 "slices" of a year to a single year by grouping the individual 12 months by each year and # calculating the mean over it. we get an array with 2 dimensions: 1 [temperature] x 40 [years] T2_w_latlon_group = T2_w_latlon.groupby('time.year').mean(dim = 'time') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # now we combine the temperature (see above) and the CO2 concentration in one plot: # create figure and axis objects with subplots() fig, ax = plt.subplots() # make a plot and add legend data1, = ax.plot(ds_co2_mm_gl_yeas.average, color="red", label = 'Mean Global CO$_2$ Concentration [ppm]') # set x- and y-axis label ax.set_xlabel("time [years]") ax.set_ylabel("CO$_2$ Concentration [ppm]", color="red") # add a second axis ax1=ax.twinx() # plot the data data2, = ax1.plot(T2_w_latlon_group, label='Mean Global Temperature [°C]') # add the label of the second y-axis. Note that we dont need a x-axis-label, because we already did that before ax1.set_ylabel("Temperature [°C]", color="blue") # place legend and title ax.legend(loc = 'upper left', title='Legend') ax1.legend(loc='upper left', bbox_to_anchor=(0, 0.88)); # Note that this isn't an elegant solution at all. plt.title('Annual Global CO$_2$ Concentration [ppm] and Temperature [°C] from 1979-2022 (Note that the Temperature values are only recorded until 2018)'); |
Part 5: variability and ENSO (open research question)¶
Using the available data, describe the global effect of an El Niño year and a La Niña year on sea-surface temperature, precipitation, and air temperature. I 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. Then use annual or seasonal anomaly maps to show the patterns of SST, temperature and precipitation anomalies during these events. With citation or links, explain why you picked these years as examples.
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | # open dataset of SST (1979-2019) ds_ERA5_sst = xr.open_dataset('C:/Users/Mazzurana/Desktop/Uni_Atmosphaerenwissenschaften/Semester_5/Climate_Systems/Python/Data/ERA5_LowRes_Monthly_sst.nc') # furthermore we have # ds_ERA5_T2m : surface temperature (1979-2019) # ds_ERA5_tp : precipitation (1979-2019) # first we select the year we want to concentrate on (in our case 1998(el nino)-1999(la nina)) # for finding the most significant el nino and la nina years we decided to rely on the following website # http://meteora.ucsd.edu/~pierce/elnino/history.html # air temperature ds_t2m_1997_98 = ds_ERA5_T2m.sel(time = ds_ERA5_sst.time.dt.year.isin([1997,1998])) ds_t2m_1999_00 = ds_ERA5_T2m.sel(time = ds_ERA5_sst.time.dt.year.isin([1998,2000])) # sea surface temperature ds_sst_1997_98 = ds_ERA5_sst.sel(time = ds_ERA5_sst.time.dt.year.isin([1997,1998])) ds_sst_1999_00 = ds_ERA5_sst.sel(time = ds_ERA5_sst.time.dt.year.isin([1999,2000])) # precipitation ds_tp_1997_98 = ds_ERA5_tp.sel(time = ds_ERA5_sst.time.dt.year.isin([1997,1998])) ds_tp_1999_00 = ds_ERA5_tp.sel(time = ds_ERA5_sst.time.dt.year.isin([1999,2000])) # compute average over the year for every dataset # air temperature + convert to °C ds_t2m_9798_mean = ds_t2m_1997_98.t2m.mean(dim = 'time') - 273.15 ds_t2m_9900_mean = ds_t2m_1999_00.t2m.mean(dim = 'time') - 273.15 # sst + convert to °C ds_sst_9798_mean = ds_sst_1997_98.sst.mean(dim = 'time') - 273.15 ds_sst_9900_mean = ds_sst_1999_00.sst.mean(dim = 'time') - 273.15 # precipitation + convert to mm/d ds_tp_9798_mean = ds_tp_1997_98.tp.mean(dim = 'time') * 1000 ds_tp_9900_mean = ds_tp_1999_00.tp.mean(dim = 'time') * 1000 # now calculate the anomalies of the precipitations orientated at the mean over the whole measurement period (1979-2019) # Note: the mean was calculated in section 1 of the project # air temperature anomalies: t2m_mean_anomaly_9798 = ds_t2m_9798_mean - Tc2m_mean.mean(dim='longitude') t2m_mean_anomaly_9900 = ds_t2m_9900_mean - Tc2m_mean.mean(dim='longitude') # sea surface temperature # before we can calculate the anomaly of the sea surface temperature, we have to calculate the mean of the whole 40 years SST_mean = ds_ERA5_sst.sst.mean(dim='time') SSTc_mean = SST_mean - 273.15 # convert to °C # now we can compute the anomaly as above: sst_mean_anomaly_9798 = ds_sst_9798_mean - SSTc_mean.mean(dim='longitude') sst_mean_anomaly_9900 = ds_sst_9900_mean - SSTc_mean.mean(dim='longitude') # precipitation # same as sst; the mean has to be calculated before: tp_mean = ds_ERA5_tp.tp.mean(dim='time') tpmm_mean = tp_mean * 1000 # convert from m/d to mm/d # anomalies: tp_mean_anomaly_9798 = ds_tp_9798_mean - tpmm_mean.mean(dim='longitude') tp_mean_anomaly_9900 = ds_tp_9900_mean - tpmm_mean.mean(dim='longitude') |
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 | # fig(a): computed mean temperature on a global scale over the entire dataset, year 1997-1998 ax = plt.subplot(2, 2, 1, projection = ccrs.Robinson()) ds_t2m_98_mean.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'plasma', levels =15, cbar_kwargs = {'label':'Temperature @ 2 m [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(A) Mean Temperature [°C] (1997-1998)'); # fig(b): computed mean temperature on a global scale over the entire dataset, year 1999-2000 ax = plt.subplot(2, 2, 2, projection = ccrs.Robinson()) ds_t2m_99_mean.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'plasma', levels =15, cbar_kwargs = {'label':'Temperature @ 2 m [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(B) Mean Temperature [°C] (1999-2000)'); # fig(c): computed temperature anomaly [°C] on a global scale over the entire dataset, year 1997-1998 ax = plt.subplot(2, 2, 3, projection = ccrs.Robinson()) t2m_mean_anomaly_9798.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'plasma', levels =15, cbar_kwargs = {'label':'Temperature @ 2 m [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(C) Mean Temperature Anomaly [°C] (1997-1998)'); # fig(d): computed mean temperature anomaly [°C] on a global scale over the entire dataset, year 1999-2000 ax = plt.subplot(2, 2, 4, projection = ccrs.Robinson()) t2m_mean_anomaly_9900.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'plasma', levels =15, cbar_kwargs = {'label':'Temperature @ 2 m [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(D) Mean Temperature Anomaly [°C] (1999-2000)'); |
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 | # fig(e): computed mean sea surface temperature on a global scale over the entire dataset, year 1997-1998 ax = plt.subplot(2, 2, 1, projection = ccrs.Robinson()) ds_sst_98_mean.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'plasma', levels =15, cbar_kwargs = {'label':'Sea Surface Temperature [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(E) Mean Sea Surface Temperature [°C] (1997-1998)'); # fig(f): computed mean sea level temperature on a global scale over the entire dataset, year 1999-2000 ax = plt.subplot(2, 2, 2, projection = ccrs.Robinson()) ds_sst_99_mean.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'plasma', levels =15, cbar_kwargs = {'label':'Sea Surface Temperature [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(F) Mean Sea Surface Temperature [°C] (1999-2000)'); # fig(g): computed mean sea surface temperature anomaly [°C] on a global scale over the entire dataset, year 1997-1998 ax = plt.subplot(2, 2, 3, projection = ccrs.Robinson()) sst_mean_anomaly_9798.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'plasma', levels =15, cbar_kwargs = {'label':'Sea Surface Temperature [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(G) Mean Sea Surface Temperature Anomaly [°C] (1997-1998)'); # fig(h): computed mean sea surface temperature anomaly [°C] on a global scale over the entire dataset, year 1999-2000 ax = plt.subplot(2, 2, 4, projection = ccrs.Robinson()) sst_mean_anomaly_9900.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'plasma', levels =15, cbar_kwargs = {'label':'Sea Surface Temperature [°C]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(H) Mean Sea Surface Temperature Anomaly [°C] (1999-2000)'); |
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 | # fig(i): computed mean precipitation [mm/d] on a global scale over the entire dataset, year 1997-1998 ax = plt.subplot(2, 2, 1, projection = ccrs.Robinson(central_longitude=180)) ds_tp_98_mean.plot(ax=ax, transform=ccrs.PlateCarree(central_longitude=180),cmap = 'ylgnbu', levels =15, cbar_kwargs = {'label':'Annual Precipitation [mm/d]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(I) Mean Annula Precipitation [mm/d] (1997-1998)'); # fig(j): computed mean precipitation [mm/d] on a global scale over the entire dataset, year 1999-2000 ax = plt.subplot(2, 2, 2, projection = ccrs.Robinson()) ds_tp_99_mean.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'ylgnbu', levels =15, cbar_kwargs = {'label':'Annual Precipitation [mm/d]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(J) Mean Annula Precipitation [mm/d] (1999-2000)'); # fig(k): computed mean precipitation anomaly [mm/d] on a global scale over the entire dataset, year 1997-1998 ax = plt.subplot(2, 2, 3, projection = ccrs.Robinson()) sst_mean_anomaly_9798.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'ylgnbu', levels =15, cbar_kwargs = {'label':'Annual Precipitation [mm/d]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(K) Mean Annula Precipitation Anomaly [mm/d] (1997-1998)'); # fig(l): computed mean precipitation anomaly [mm/d] on a global scale over the entire dataset, year 1999-2000 ax = plt.subplot(2, 2, 4, projection = ccrs.Robinson()) sst_mean_anomaly_9900.plot(ax=ax, transform=ccrs.PlateCarree(),cmap = 'ylgnbu', levels =15, cbar_kwargs = {'label':'Annual Precipitation [mm/d]'}) ax.coastlines(); ax.gridlines(color = 'grey', linestyle = '--'); #ax.ylabels_right = ax.xlabels_top = False; plt.title('(L) Mean Annula Precipitation Anomaly [mm/d] (1999-2000)'); |
For the following analysis of temperature, precipitation and air temperature, we decided to use the data of the El Nino event which occured in the period between June 1997 and June 1998. This was because of the heavy temperature increase of the Atlantic, as seen in Plot (G), in a depth of about 150m below surface. The rise in temperature at this waterlevel is typical during the event of an El Nino, but in this timespawn the increase was about 1,5°C above normal, which caused high damage when it comes to reefs or general flooding of the involved coasts and countries of Ecuador, Peru, Bolivia, Somalia and Kenya. Furthermore it was, until this time, the most powerful event in history of meteorological records.
For the La Nina event we choose the time between 1999 and 2000, which was again a significant one because of the duration of the event. The actual timespawn was between 1998 and 2000, but the values for 1998 and 1999 are presentable as almost equal in anomaly. Therefore we decided to take just those two years. Beside this, one of the factors for such a strong event were the abnormally warm winter conditions over North America as seen in Plot (D). Compared to the El Nino event jsut in the years beforen (1997-1998), the anomalies state again, this time, a decrease of temperature at around 1,5°C.
Refernces:
El Nino - Neelin, J. D., & Latif, M. (1998). El Niño dynamics. Physics Today, 51(12), 32.
La Nina - https://agupubs.onlinelibrary.wiley.com/doi/full/10.1029/2008JD011185
1 |