{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"[](https://colab.research.google.com/github/neuromatch/climate-course-content/blob/main/tutorials/W1D5_IntroductiontoClimateModeling/student/W1D5_Tutorial3.ipynb)
\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Tutorial 3: A Zero-Dimensional Energy Balance Model\n",
"\n",
"**Week 1, Day 5, Introduction to Climate Modeling**\n",
"\n",
"**Content creators:** Jenna Pearson, Brian E. J. Rose\n",
"\n",
"**Content reviewers:** Mujeeb Abdulfatai, Nkongho Ayuketang Arreyndip, Jeffrey N. A. Aryee, Younkap Nina Duplex, Zahra Khodakaramimaghsoud, Will Gregory, Paul Heubel, Zahra Khodakaramimaghsoud, Peter Ohue, Agustina Pesce, Abel Shibu, Derick Temfack, Yunlong Xu, Peizhen Yang, Chi Zhang, Ohad Zivan\n",
"\n",
"**Content editors:** Paul Heubel, Brodie Pearson, Abigail Bodner, Ohad Zivan, Chi Zhang\n",
"\n",
"**Production editors:** Wesley Banfield, Paul Heubel, Jenna Pearson, Konstantine Tsafatinos, Chi Zhang, Ohad Zivan\n",
"\n",
"**Our 2024 Sponsors:** CMIP, NFDI4Earth\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Tutorial Objectives\n",
"\n",
"*Estimated timing of tutorial:* 30 minutes\n",
"\n",
"In this tutorial students will learn about the heat capacity of the atmosphere and oceans, how this related to temperature changes over time, and set up their first climate model.\n",
"\n",
"By the end of this tutorial students will be able to:\n",
"\n",
"- Calculate the heat capacity of the ocean and atmosphere.\n",
"- Create and run a time-dependent model of the change in global mean surface temperature in response to energy imbalances.\n",
"- Describe the influence of transmissivity and albedo on the equilibrium temperature from this model.\n",
"- Bonus: Understand what equilibrium climate sensitivity is and how to find it using the model.\n",
"\n",
"*This tutorial is based on content from [The Climate Laboratory](https://brian-rose.github.io/ClimateLaboratoryBook/) by Brian E. J. Rose.*"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Setup\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {},
"tags": [
"colab"
]
},
"outputs": [],
"source": [
"# imports\n",
"import numpy as np # used for algebra and array operations\n",
"import matplotlib.pyplot as plt # used for plotting"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Install and import feedback gadget\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Install and import feedback gadget\n",
"\n",
"!pip3 install vibecheck datatops --quiet\n",
"\n",
"from vibecheck import DatatopsContentReviewContainer\n",
"def content_review(notebook_section: str):\n",
" return DatatopsContentReviewContainer(\n",
" \"\", # No text prompt\n",
" notebook_section,\n",
" {\n",
" \"url\": \"https://pmyvdlilci.execute-api.us-east-1.amazonaws.com/klab\",\n",
" \"name\": \"comptools_4clim\",\n",
" \"user_key\": \"l5jpxuee\",\n",
" },\n",
" ).render()\n",
"\n",
"\n",
"feedback_prefix = \"W1D5_T3\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Figure settings\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Figure settings\n",
"import ipywidgets as widgets # interactive display\n",
"\n",
"%config InlineBackend.figure_format = 'retina'\n",
"plt.style.use(\n",
" \"https://raw.githubusercontent.com/neuromatch/climate-course-content/main/cma.mplstyle\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 1: A Zero-Dimensional Energy Balance Model\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 1: A Zero-Dimensional Energy Balance Model\n",
"\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == \"Bilibili\":\n",
" src = f\"https://player.bilibili.com/player.html?bvid={id}&page={page}\"\n",
" elif source == \"Osf\":\n",
" src = f\"https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render\"\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == \"Youtube\":\n",
" video = YouTubeVideo(\n",
" id=video_ids[i][1], width=W, height=H, fs=fs, rel=0\n",
" )\n",
" print(f\"Video available at https://youtube.com/watch?v={video.id}\")\n",
" else:\n",
" video = PlayVideo(\n",
" id=video_ids[i][1],\n",
" source=video_ids[i][0],\n",
" width=W,\n",
" height=H,\n",
" fs=fs,\n",
" autoplay=False,\n",
" )\n",
" if video_ids[i][0] == \"Bilibili\":\n",
" print(\n",
" f\"Video available at https://www.bilibili.com/video/{video.id}\"\n",
" )\n",
" elif video_ids[i][0] == \"Osf\":\n",
" print(f\"Video available at https://osf.io/{video.id}\")\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [(\"Youtube\", \"aX34LHt0DTg\"), (\"Bilibili\", \"BV1514y1R7Dt\")]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_A_Zero_Dimensional_Energy_Balance_Model_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"pycharm": {
"name": "#%%\n"
},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @markdown\n",
"from ipywidgets import widgets\n",
"from IPython.display import IFrame\n",
"\n",
"link_id = \"5mgns\"\n",
"\n",
"print(f\"If you want to download the slides: https://osf.io/download/{link_id}/\")\n",
"IFrame(src=f\"https://mfr.ca-1.osf.io/render?url=https://osf.io/{link_id}/?direct%26mode=render%26action=download%26mode=render\", width=854, height=480)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_A_Zero_Dimensional_Energy_Balance_Model_Slides\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Section 1: Building the Model\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Section 1.1: Moving Forward in Time\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"One of the crucial things missing from the simple model we have looked at so far is its ability to _change with time_. \n",
"\n",
"As the composition of the atmosphere (among other things) changes, in response, so does the energy balance and global mean surface temperature. This is shown by the thick black lines in the figure below, where the time series of observed global mean surface air temperature change from the 1850-1900 reference period is plotted. Figures like this are called 'hockey stick' figures due to their shape: a relatively stable period followed by a steep increase."
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"\n",
"Figure 3.4 | Observed and simulated time series of the anomalies in annual and global mean surface air temperature (GSAT). All anomalies are differences from the 1850–1900 time mean of each individual time series. The reference period 1850–1900 is indicated by grey shading. (a) Single simulations from CMIP6 models (thin lines) and the multi-model mean (thick red line). Observational data (thick black lines) are from the Met Office Hadley Centre/Climatic Research Unit dataset (HadCRUT5), and are blended surface temperature (2 m air temperature over land and sea surface temperature over the ocean). All models have been subsampled using the HadCRUT5 observational data mask. Vertical lines indicate large historical volcanic eruptions. CMIP6 models which are marked with an asterisk are either tuned to reproduce observed warming directly, or indirectly by tuning equilibrium climate sensitivity. Inset: GSAT for each model over the reference period, not masked to any observations. (b) Multi-model means of CMIP5 (blue line) and CMIP6 (red line) ensembles and associated 5th to 95th percentile ranges (shaded regions). Observational data are HadCRUT5, Berkeley Earth, National Oceanic and Atmospheric Administration NOAAGlobalTemp-Interim and Kadow et al. (2020). Masking was done as in (a). CMIP6 historical simulations were extended with SSP2-4.5 simulations for the period 2015–2020 and CMIP5 simulations were extended with RCP4.5 simulations for the period 2006–2020. All available ensemble members were used (see Section 3.2). The multi-model means and percentiles were calculated solely from simulations available for the whole time span (1850–2020). The figure is updated from Bock et al. (2020), their Figures 1 and 2. CC BY 4.0 https://creativecommons.org/licenses/by/4.0/. Further details on data sources and processing are available in the chapter data table (Table 3.SM.1). (Credit: [IPCC Report AR6](https://www.ipcc.ch/report/ar6/wg1/downloads/figures/IPCC_AR6_WGI_Figure_3_4.png))\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"In order to incorporate time-dependent behavior into our model, we need to include a mathematical representation of how the _change in energy_ relates to a _change in temperature_ over time.\n",
"\n",
"We can represent the change in temperature over time as the net heat input or loss from radiation ($ASR-OLR$) multiplied by the heat capacity of the Earth system.\n",
"\n",
"As we learned in Day 2 of this week, the heat capacity of a medium is its ability to increase in temperature given an input of heat. Not all components of the Earth system (for example land, ocean, atmosphere) have the same heat capacity.\n",
"\n",
"Mathematically, the rate of change of global mean surface temperature ($T$) over time is given as\n",
"\n",
"\\begin{align*}\n",
"\\text{rate of change of }T &= \\frac{1}{\\text{heat capacity}}\\cdot (\\text{energy flux in - energy flux out}) \\\\\n",
"&= \\frac{1}{C} \\cdot ASR - OLR\n",
"\\end{align*}\n",
"\n",
"where $C$ is the heat capacity of the Earth system. Note here that when $ASR > OLR$, then the system is gaining heat. Conversely when $ASR < OLR$, then the system is losing heat over time.\n",
"\n",
"To calculate the heat capacity for the Earth system, we will assume that it is a combination of atmosphere and ocean only, that is $C = C_{\\text{oc}} + C_{\\text{atmo}}$.\n",
"\n",
"Generally, the heat capacity of a medium is the specific heat of that medium times the total mass.\n",
"\n",
"For the atmosphere, we have $C_{\\text{atm}} = c_{\\text{atm}} \\cdot \\frac{W_{\\text{atm}}}{g} $ where $c_{\\text{atm}}$ is the specific heat of the atmosphere, $W_{\\text{atm}}$ is the weight of a column of air, and $g$ is the acceleration due to gravity.\n",
"\n",
"For the ocean we have $C_{\\text{oc}} = c_{\\text{oc}} \\cdot \\rho_{\\text{oc}} \\cdot d_{\\text{oc}}$ where $c_{\\text{oc}}$ is the specific heat of the ocean, $\\rho_{\\text{oc}}$ is the density of seawater, and $d_{\\text{oc}}$ is a representative depth of the ocean.\n",
"\n",
"We will use these definitions to find the heat capacities of the atmosphere and ocean and to refresh what we learned on Day 2.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# heat capacity of the ocean\n",
"c_oc = 3850 # specific heat of seawater in J/kg/K\n",
"rho_oc = 1025 # average density of seawater in kg/m3\n",
"d_oc = 70 # depth of water in m (here representative of the mixed layer)\n",
"C_oc = c_oc * rho_oc * d_oc # heat capacity of the ocean\n",
"\n",
"# heat capacity of the atmosphere\n",
"c_atm = 1004 # specific heat of the atmosphere at constant pressure in J/kg/K\n",
"W_atm = 100000 # weight (pressure) of atmospheric column in Pa\n",
"g = 9.81 # acceleration due to gravity in m/s^2\n",
"C_atm = c_atm * (W_atm / g) # heat capacity of the atmosphere\n",
"\n",
"# total heat capacity\n",
"C = C_oc + C_atm\n",
"\n",
"# print results\n",
"print(f'Ocean Heat Capacity: {C_oc:.2f} J m^-2 K^-1')\n",
"print(f'Atmosphere Heat Capacity: {C_atm:.2f} J m^-2 K^-1')\n",
"print(f'Total Heat Capacity: {C:.2f} J m^-2 K^-1')"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"### Coding Exercises 1.1\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"1. Calculate the depth of the ocean needed for the ocean to have the same heat capacity as the atmosphere.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {},
"executionInfo": {
"elapsed": 308,
"status": "ok",
"timestamp": 1681356188649,
"user": {
"displayName": "Jenna Pearson",
"userId": "05648130581702734913"
},
"user_tz": 420
}
},
"outputs": [],
"source": [
"# heat capacity of the atmosphere\n",
"c_atm = 1004 # specific heat of the atmosphere at constant pressure in J/kg/K\n",
"W_atm = 100000 # weight (pressure) of atmospheric column in Pa\n",
"g = 9.81 # height of atmosphere in m (representative of )\n",
"C_atm = c_atm * (W_atm / g) # heat capacity of the atmosphere\n",
"\n",
"# find the depth of the ocean for equivalent atmospheric heat capacity\n",
"c_oc = 3850 # specific heat of seawater in J/kg/K\n",
"rho_oc = 1025 # average density of seawater in kg/m3\n",
"\n",
"d_oc = ...\n",
"d_oc"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {},
"executionInfo": {
"elapsed": 310,
"status": "ok",
"timestamp": 1681356188952,
"user": {
"displayName": "Jenna Pearson",
"userId": "05648130581702734913"
},
"user_tz": 420
}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_a1d313aa.py)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Coding_Exercises_1_1\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"### Questions 1.1: Climate Connection\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"1. In your own words, describe what the answer to your coding exercise means.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_a860c046.py)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Questions_1_1\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Section 1.2: The Numerical Model\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"Knowing the heat capacity, and the descriptions of $OLR$ and $ASR$ from previous tutorials, we can write the equation\n",
"\n",
"First note that \n",
"\\begin{align}\n",
"\\text{rate of change }T = \\frac{\\text{change in }T}{\\text{change in time}}=\\frac{dT}{dt}\n",
"\\end{align}\n",
"\n",
"So the equation for our model can be written\n",
"\\begin{align}\n",
"\\frac{dT}{dt}= \\frac{1}{C}(ASR - OLR)\n",
"\\end{align}\n",
"\n",
"Numerically, we can use this equation to compute the global mean surface temperature after a small interval of time by adding on the amount of energy gained or lost multiplied by the time interval itself.\n",
"\n",
"The particular method of numerically defining the time and temperature intervals (changes) is called **[discretization](https://glossarytest.ametsoc.net/wiki/Discretization)**, and the way we have chosen to do this is called the **Forward Euler method**. The exact details of this method are beyond the scope of this tutorial, and we will use the method without further elaboration.\n",
"\n",
"The Forward Euler method assumes we can use $\\text{change in }T = T_{n+1} - T_{n}$ and $\\text{change in t} = t_{n+1} - t_{n}$ where $t$ is time. Thus, if we know the time interval and the current temperature $\\left(T_n\\right)$, we can predict the temperature at the end of our time interval, $\\left(T_{n+1}\\right)$.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# define the time interval, currently one year expressed in seconds\n",
"dt = 60.0 * 60.0 * 24.0 * 365.0\n",
"\n",
"# define albedo\n",
"alpha = 0.2941 # unitless number between 0 and 1 (calculated previously from observations in tutorial 2)\n",
"\n",
"# define transmissivity (calculated previously from observations in tutorial 1)\n",
"tau = 0.6127 # unitless number between 0 and 1\n",
"\n",
"\n",
"# define a function for absorbed shortwave radiation (ASR)\n",
"def ASR(alpha, Q):\n",
" return (1 - alpha) * Q\n",
"\n",
"\n",
"# define a function for outgoing longwave radiation (OLR)\n",
"def OLR(tau, T):\n",
" # define the Stefan-Boltzmann Constant, noting we are using 'e' for scientific notation\n",
" sigma = 5.67e-8 # W m^-2 K^-4\n",
"\n",
" return tau * sigma * T**4\n",
"\n",
"\n",
"# create a function to find the new temperature based on the previous using Euler's method.\n",
"def step_forward(T, alpha, tau, dt):\n",
" # define the observed insolation based on observations from the IPCC AR6 Figure 7.2\n",
" Q = 340 # W m^-2\n",
"\n",
" # find the new temperature using forward Euler method\n",
" T_new = T + dt / C * (ASR(alpha, Q) - OLR(tau, T))\n",
"\n",
" return T_new"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"We can now use a loop to apply this function many times over by specifying an initial temperature and a time interval. Note we will be using [lists](https://docs.python.org/3/tutorial/datastructures.html) to do so.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {},
"executionInfo": {
"elapsed": 6,
"status": "ok",
"timestamp": 1681356197368,
"user": {
"displayName": "Jenna Pearson",
"userId": "05648130581702734913"
},
"user_tz": 420
}
},
"outputs": [],
"source": [
"# define the number of timesteps (currently years) to run the model\n",
"numtsteps = 15\n",
"\n",
"# for converting number of seconds in a year\n",
"sec_2_yr = 3.154e7\n",
"\n",
"# set the intial temperature (initial condition)\n",
"T_series = [288]\n",
"\n",
"# set the initial time to 0\n",
"t_series = [0]\n",
"\n",
"# run the model\n",
"for n in range(numtsteps):\n",
" # calculate and append the time since running the model, dependent on dt and the numtsteps\n",
" t_series.append((n + 1) * dt / sec_2_yr)\n",
"\n",
" # calculate and append the new temperature using our pre-defined function\n",
" T_series.append(step_forward(T_series[n], alpha=alpha, tau=tau, dt=dt))\n",
"\n",
"# display the temeprature time series\n",
"print(T_series)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {},
"executionInfo": {
"elapsed": 177,
"status": "ok",
"timestamp": 1681356200832,
"user": {
"displayName": "Jenna Pearson",
"userId": "05648130581702734913"
},
"user_tz": 420
}
},
"outputs": [],
"source": [
"# display the time series\n",
"print(t_series)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {},
"executionInfo": {
"elapsed": 921,
"status": "ok",
"timestamp": 1681356201977,
"user": {
"displayName": "Jenna Pearson",
"userId": "05648130581702734913"
},
"user_tz": 420
}
},
"outputs": [],
"source": [
"# plot the results\n",
"fig, ax = plt.subplots()\n",
"ax.plot(t_series, T_series)\n",
"ax.set_xlabel(\"Time (years)\")\n",
"ax.set_ylabel(\"Global mean temperature (K)\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"### Questions 1.2\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"1. Do you think the time step (interval) we have used will affect the solution? If so, how?\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_7d35dd95.py)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Questions_1_2\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"### Coding Exercise 1.2\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"1. Using a for loop, run the model for 15 years with three different intervals ($dt$) of a half year, 1 year, and 5 years and plot the results. Note you will have to change the number of timesteps used when changing `dt` so that the model runs for the same amount of time. Plot your results on the same figure.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {},
"executionInfo": {
"elapsed": 165,
"status": "error",
"timestamp": 1681356207569,
"user": {
"displayName": "Jenna Pearson",
"userId": "05648130581702734913"
},
"user_tz": 420
}
},
"outputs": [],
"source": [
"# one year expressed in seconds\n",
"one_yr = 60.0 * 60.0 * 24.0 * 365.0\n",
"\n",
"# legend labels\n",
"labels = [\"dt = half-year\", \"dt = one year\", \"dt = five years\"]\n",
"\n",
"# define the number of timesteps (years) to run the model\n",
"numtsteps = np.array([10, 5, 1]) * 3\n",
"\n",
"# for converting the number of seconds in a year\n",
"sec_2_yr = 3.154e7\n",
"\n",
"fig, ax = plt.subplots()\n",
"# loop through each choice of time step\n",
"for dd, dt_2 in enumerate([one_yr * 0.5, one_yr, one_yr * 5]):\n",
" # set the intial temperature (initial condition)\n",
" ...\n",
"\n",
" # set the initial time to 0\n",
" ...\n",
"\n",
" # run the model\n",
" for n in range(numtsteps[dd]):\n",
" # calculate and append the time since running the model, dependent on dt and the numtsteps\n",
" ...\n",
"\n",
" # calculate and append the new temperature using our pre-defined function step_forward()\n",
" ...\n",
"\n",
" ax.plot(t_series, T_series, label=labels[dd])\n",
"\n",
"ax.set_xlabel(\"Time (years)\")\n",
"ax.set_ylabel(\"Global mean temperature (K)\")\n",
"ax.legend()"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {},
"executionInfo": {
"elapsed": 1096,
"status": "ok",
"timestamp": 1681356209279,
"user": {
"displayName": "Jenna Pearson",
"userId": "05648130581702734913"
},
"user_tz": 420
}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_5e19a97e.py)\n",
"\n",
"*Example output:*\n",
"\n",
"
\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Coding_Exercises_1_2\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Section 2: Revisiting the Climate Change Scenario from Tutorial 3\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Section 2.1: Enhanced Greenhouse Effect\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"In Tutorial 2 we looked at how changing the transmissivity $\\left(\\tau\\right)$ affected the equilibrium temperature $T_{\\text{eq}}$. Now we can use our time-dependent model to investigate this in more depth. Reuse the model, this time setting $\\tau = 0.57$.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {},
"executionInfo": {
"elapsed": 942,
"status": "ok",
"timestamp": 1681356214169,
"user": {
"displayName": "Jenna Pearson",
"userId": "05648130581702734913"
},
"user_tz": 420
}
},
"outputs": [],
"source": [
"# define transmissivity (calculated previously from observations)\n",
"tau_2 = 0.57 # unitless number between 0 and 1\n",
"\n",
"# define the number of timesteps (currently years) to run the model\n",
"numtsteps = 15\n",
"\n",
"# set the intial temperature (initial condition)\n",
"T_series = [288]\n",
"\n",
"# set the initial time to 0\n",
"t_series = [0]\n",
"\n",
"# run the model\n",
"for n in range(numtsteps):\n",
" # calculate and append the time since running the model, dependent on dt and the numtsteps\n",
" t_series.append((n + 1) * dt / sec_2_yr)\n",
"\n",
" # calculate and append the new temperature using our pre-defined function\n",
" T_series.append(step_forward(T_series[n], alpha=alpha, tau=tau_2, dt=dt))\n",
"\n",
"fig, ax = plt.subplots()\n",
"ax.plot(t_series, T_series)\n",
"\n",
"ax.set_xlabel(\"Time (years)\")\n",
"ax.set_ylabel(\"Global mean temperature (K)\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"### Questions 2.1\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"1. How does the long-term temperature $T_{\\text{eq}}$ here compare to the value you found from Tutorial 2?\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_caa2deb1.py)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Questions_2_1\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Bonus Coding Exercise: Equilibrium Climate Sensitivity\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"Here we define the **[equilibrium climate sensitivity](https://www.ipcc.ch/report/ar6/wg1/downloads/report/IPCC_AR6_WGI_AnnexVII.pdf)** as the long-term global warming (equilibrium temperature increase) caused by a doubling of carbon dioxide above its pre-industrial concentration. The impact of a doubling of carbon dioxide on these energy flows is measured by a [radiative forcing](https://www.ipcc.ch/report/ar6/wg1/downloads/report/IPCC_AR6_WGI_AnnexVII.pdf). Here a positive radiation forcing leads to warming, and a negative radiative forcing leads to cooling.\n",
"\n",
"The equilibrium climate sensitivity depends on a number of things, including physics and feedbacks of the model used. In the following exercise, you will calculate the equilibrium climate sensitivity of _our_ model.\n",
"\n",
"1. Write a function called `step_forward()` as from above, and then create another function that adds in a radiative forcing to the difference between $ASR$ and $OLR$ and call it `step_forward_forced()`. Make sure both functions output the energy balance of the model. Consistent with the [IPCC AR6](https://www.ipcc.ch/report/ar6/wg1/chapter/chapter-7/#Effective), use an effective radiative forcing of 3.93 W m$^{-2}$, where _effective_ means the climate system, but not the surface temperature, has been allowed to adjust.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# define your functions and constants\n",
"\n",
"# define albedo\n",
"alpha = 0.2941 # unitless number between 0 and 1 (calculated previously from observations in tutorial 2)\n",
"\n",
"# define transmissivity (calculated previously from observations in tutorial 1)\n",
"tau = 0.6127 # unitless number between 0 and 1\n",
"\n",
"# effective radiative forcing for a doubling of CO2\n",
"F = 3.93 # W/m^2\n",
"\n",
"# define the time interval, one year expressed in seconds\n",
"dt = 60.0 * 60.0 * 24.0 * 365.0\n",
"\n",
"# for converting number of seconds in a year\n",
"sec_2_yr = 3.154e7\n",
"\n",
"\n",
"# define a function for absorbed shortwave radiation (ASR)\n",
"def ASR(alpha, Q):\n",
" return (1 - alpha) * Q\n",
"\n",
"\n",
"# define a function for outgoing longwave radiation (OLR)\n",
"def OLR(tau, T):\n",
" # define the Stefan-Boltzmann Constant, noting we are using 'e' for scientific notation\n",
" sigma = 5.67e-8 # W m^-2 K^-4\n",
"\n",
" return tau * sigma * T**4\n",
"\n",
"\n",
"# create a function to find the new temperature based on the previous using Euler's method.\n",
"def step_forward(T, alpha, tau, dt):\n",
" # define the observed insolation based on observations from the IPCC AR6 Figure 7.2\n",
" ...\n",
"\n",
" # define the Stefan-Boltzmann Constant, noting we are using 'e' for scientific notation\n",
" ...\n",
"\n",
" Ftoa = ...\n",
"\n",
" T_new = ...\n",
"\n",
" return ...\n",
"\n",
"\n",
"# create a function to find the new temperature based on the previous using Euler's method.\n",
"def step_forward_forced(T, alpha, tau, dt):\n",
" # define the observed insolation based on observations from the IPCC AR6 Figure 7.2\n",
" ...\n",
"\n",
" # define the Stefan-Boltzmann Constant, noting we are using 'e' for scientific notation\n",
" ...\n",
"\n",
" Ftoa = ...\n",
"\n",
" T_new = ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_71e39306.py)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Bonus_Coding_Exercises_3_1\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"2. Using an initial temperature of 288 K, run the model to equilibrium. Make sure your model is in equilbrium by checking that the energy balance is near zero.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# run the model to equilibrium without forcing and begining with T(0) = 288K\n",
"\n",
"# define the number of timesteps (years) to run the model\n",
"numtsteps = 40\n",
"\n",
"# set the intial temperature (initial condition)\n",
"T_series = [288]\n",
"\n",
"# set the initial time to 0\n",
"t_series = [0]\n",
"\n",
"# run the model\n",
"for n in range(numtsteps):\n",
" # calculate and append the time since running the model, dependent on dt and the numtsteps\n",
" t_series.append(...)\n",
"\n",
" # calculate and append the new temperature using our pre-defined function and get energy balance\n",
" ...\n",
" T_series.append(...)\n",
"\n",
"print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_35a3c870.py)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Bonus_Coding_Exercises_3_2\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"3. Run the forced model equilibrium using the unforced equilibrium temperature as your inital condition and the `step_forward_forced()` function you wrote above.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# define the number of timesteps (years) to run the model\n",
"numtsteps = 40\n",
"\n",
"# set initial condition (temperature) to the equilibrium value from the last run without forcing\n",
"T_series_forced = [T_series[-1]]\n",
"\n",
"# set the initial time to 0\n",
"t_series_forced = [0]\n",
"\n",
"# run the model\n",
"for n in range(numtsteps):\n",
" # calculate and append the time since running the model, dependent on dt and the numtsteps\n",
" t_series_forced.append(...)\n",
"\n",
" # calculate and append the new temperature using our pre-defined function and get energy balance\n",
" ...\n",
" T_series_forced.append(...)\n",
"\n",
"print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_d7862a90.py)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Bonus_Coding_Exercises_3_3\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"4. Plot the temperature curve from the forced simulation as a function of time.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# plot the time series\n",
"fig, ax = plt.subplots()\n",
"_ = ...\n",
"\n",
"ax.set_xlabel(\"Time (years)\")\n",
"ax.set_ylabel(\"Global mean temperature (K)\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_4ae01b99.py)\n",
"\n",
"*Example output:*\n",
"\n",
"
\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Bonus_Coding_Exercises_3_4\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"5. Subtract the intial temperature used for your forced simulation from the final temperature after running to equilibrium to get the equilibrium climate sensitivty.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# calculate equilibrium climate sensitivity\n",
"ecs = ...\n",
"print(\"Equilibrium Climate Sensitivity: \", ecs)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_b87ad7af.py)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Bonus_Coding_Exercises_3_5\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Bonus Questions: Climate Connection\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"1. How does this compare to the IPCC AR6 estimate of [equilibrium climate sensitivity of 2-5 K](https://www.ipcc.ch/report/ar6/wg1/chapter/chapter-7/#Climate)? Is it higher or lower? Note here it is a temperature difference, so the units of C in the report and K found here are interchangeable.\n",
"2. In your own words, describes what this implies with respect to global mean temperatures in our models versus those in used in the IPCC report.\n",
"3. What do you think could be missing in our model?\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D5_IntroductiontoClimateModeling/solutions/W1D5_Tutorial3_Solution_422bae10.py)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Bonus_Questions_3\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Summary\n",
"\n",
"In this tutorial, you explored the relationship between the heat capacity of the atmosphere and oceans and temperature changes over time. You learned how to calculate the heat capacity of these components and used this knowledge to develop a climate model. This model simulates the change in global mean surface temperature in response to energy imbalances. You explored the effects of transmissivity on the equilibrium temperature and discussed equilibrium climate sensitivity.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Resources"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"Useful links:\n",
"- [The Climate Laboratory](https://brian-rose.github.io/ClimateLaboratoryBook/) by Brian E. J. Rose\n",
"- [Numerical Discretization](https://glossarytest.ametsoc.net/wiki/Discretization)\n",
"- [Lists and List Comprehension](https://docs.python.org/3/tutorial/datastructures.html)\n",
"- [Chapter 7, IPCC AR6](https://www.ipcc.ch/report/ar6/wg1/chapter/chapter-7)\n",
"- [Equilibrium Climate Sensitivity, Annex VII, IPCC AR6](https://www.ipcc.ch/report/ar6/wg1/downloads/report/IPCC_AR6_WGI_AnnexVII.pdf)"
]
}
],
"metadata": {
"colab": {
"collapsed_sections": [],
"include_colab_link": true,
"name": "W1D5_Tutorial3",
"provenance": [],
"toc_visible": true
},
"kernel": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.19"
}
},
"nbformat": 4,
"nbformat_minor": 4
}