{ "cells": [ { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuromatch/climate-course-content/blob/main/tutorials/W1D5_IntroductiontoClimateModeling/student/W1D5_Tutorial4.ipynb)   \"Open" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "# Bonus Tutorial 4: Climate Feedbacks\n", "\n", "**Week 1, Day 5, Introduction to Climate Modeling**\n", "\n", "**Content creators:** Abigail Bodner, Jenna Pearson, Brodie Pearson, and Brian E. J. Rose\n", "\n", "**Content reviewers:** Mujeeb Abdulfatai, Nkongho Ayuketang Arreyndip, Jeffrey N. A. Aryee, Younkap Nina Duplex, 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:** Abigail Bodner, Paul Heubel, Brodie Pearson, 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", "*Note that this and the two following tutorials contain solely Bonus content, that serves experienced participants and for later investigation of 0- and 1-dimensional models after the course. Please check out these tutorials after finishing Tutorial 7-8 successfully*.\n", "\n", "In this tutorial, students will learn about climate feedbacks, in particular the Planck and ice-albedo feedbacks. Students will also learn about how variations in the insolation over time can affect the equilibrium temperature of Earth.\n", "\n", "By the end of this tutorial students will be able to:\n", "* Apply a temperature-dependent albedo within their existing climate model.\n", "* Understand the impact of insolation changes on the equilibrium temperature of Earth.\n", "\n", "*This tutorial draws on content from [The Climate Laboratory](https://brian-rose.github.io/ClimateLaboratoryBook/) by Brian E. J. Rose.*" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "# Setup" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": {}, "executionInfo": { "elapsed": 1179, "status": "ok", "timestamp": 1682069447180, "user": { "displayName": "Ohad Zivan", "userId": "06528534325719580785" }, "user_tz": -120 }, "tags": [ "colab" ] }, "outputs": [], "source": [ "# imports\n", "\n", "import numpy as np # used for algebra and array operations\n", "import matplotlib.pyplot as plt # used for plotting\n", "from scipy.optimize import brentq # used for numerical root-finding to get the equilibria" ] }, { "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_T4\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Figure ettings\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "executionInfo": { "elapsed": 517, "status": "ok", "timestamp": 1682069448933, "user": { "displayName": "Ohad Zivan", "userId": "06528534325719580785" }, "user_tz": -120 }, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Figure ettings\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: Climate Feedbacks\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 1: Climate Feedbacks\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(id=video_ids[i][1], width=W,\n", " height=H, fs=fs, rel=0)\n", " print(f'Video available at https://youtube.com/watch?v={video.id}')\n", " else:\n", " video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n", " height=H, fs=fs, autoplay=False)\n", " if video_ids[i][0] == 'Bilibili':\n", " print(f'Video available at https://www.bilibili.com/video/{video.id}')\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', 'IBAn4KLddqg'), ('Bilibili', 'BV1Wk4y1N7Lw')]\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}_Climate_Feedbacks_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 = \"btpwx\"\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}_Climate_Feedbacks_Slides\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "# Section 1: Ice-Albedo Feedback" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "## Section 1.1: Temperature Dependent Albedo" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "Our current model only contains one feedback, the 'Planck feedback' also called the 'Planck temperature response'. This feedback encapsulates that a warming of Earth leads to the planet emitting more energy (see Planck's law from Tutorial 1). In reality, there are many [climate feedbacks](https://www.ipcc.ch/report/ar6/wg1/downloads/report/IPCC_AR6_WGI_AnnexVII.pdf) that contribute to the Earth's net temperature change due to an energy imbalance. In this tutorial, we will focus on incorporating an **ice-albedo feedback** into our model.\n", "\n", "When the Earth's surface warms, snow and ice melt. This lowers the **albedo (**$\\mathbf{\\alpha}$**)**, because less solar radiation is reflected off Earth's surface. This lower albedo causes the climate to warm even more than if the albedo had stayed the same, increasing the snow and ice melt. This is referred to as a **positive feedback**. Positive feedbacks amplify the changes that are already occurring. This particular feedback is referred to as the **ice-albedo feedback**.\n", "\n", "A simple way to parameterize ice-albedo feedback in our model is through a temperature-dependent albedo, such as the one defined below (see the tutorial lecture slides for an explanation of why we use this function).\n", "\n", "\\begin{align}\n", "\\alpha = \\left\\{\n", " \\begin{array}{cl}\n", " 0.1 & T \\gt 300 \\text{ K} \\\\\n", " 0.1 + (0.7-0.1) \\cdot \\frac{(T-300)^2}{(240-300)^2} & 240\\text{ K} \\le T \\le 300\\text{ K} \\\\\n", " 0.7 & T \\lt 240\\text{ K}\n", " \\end{array}\n", " \\right.\n", "\\end{align}\n", "\n", "Using this new temperature-dependent albedo, we can plot the graphs of absorbed shortwave radiation $\\left(ASR\\right)$ and outgoing longwave radiation $\\left(OLR\\right)$:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": {}, "executionInfo": { "elapsed": 912, "status": "ok", "timestamp": 1681699458086, "user": { "displayName": "Jenna Pearson", "userId": "05648130581702734913" }, "user_tz": 420 } }, "outputs": [], "source": [ "# create a array ot temperatures to evaluates the ASR and OLR at\n", "T = np.arange(200, 360, 2, dtype=np.float64)\n", "\n", "# create empty arrays to fill with values later\n", "ASR_vals = np.zeros_like(T)\n", "\n", "# define the slope of the ramp function\n", "m = (0.7 - 0.3) / (280 - 250)\n", "\n", "# define the observed insolation based on observations from the IPCC AR6 Figure 7.2\n", "Q = 340 # W m^-2\n", "\n", "# define transmissivity (calculated previously from observations in tutorial 1)\n", "tau = 0.6127 # unitless number between 0 and 1\n", "\n", "# define a function for absorbed shortwave radiation (ASR)\n", "def ASR(Q, T):\n", " # define function for albedo\n", " if T >= 300: # temperature of very warm and ice free earth.\n", " alpha = 0.1 # average albedo of land and sea without ice\n", " elif T > 240: # temperature of Earth to sustain permafrost and sea ice everywhere.\n", " alpha = 0.1 + (0.7 - 0.1) * (T - 300) ** 2 / (240 - 300) ** 2\n", " else:\n", " alpha = 0.7 # average albedo of land and sea ice\n", " return (1 - alpha) * Q\n", "\n", "\n", "# define a function for outgoing longwave raditation (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", "# calculate OLR for different values of T\n", "OLR_vals = OLR(tau, T)\n", "\n", "# calculate ASR for different values of T\n", "for tt, temp in enumerate(T):\n", " ASR_vals[tt] = ASR(Q, temp)\n", "\n", "# make plots\n", "fig, ax = plt.subplots()\n", "ax.plot(T, ASR_vals, label=\"Absorbed Shortwave Radiation ($ASR$)\")\n", "ax.plot(T, OLR_vals, label=\"Outgoing Longwave Radiation ($OLR$)\")\n", "\n", "\n", "ax.set_xlabel(\"Temperature (K)\")\n", "ax.set_ylabel(\"Radiative Flux (Wm$^{-2}$)\")\n", "ax.legend()" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "### Questions 1.1: Climate Connection" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "1. How many times do the graphs of ASR and OLR intersect?\n", "2. What does this intersection mean in terms of Earth's energy (im)balance?" ] }, { "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_Tutorial4_Solution_197ae6c0.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: Multiple Equilibria From Graphs" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "\n", "**Equilibrium temperatures** are solutions to the model equation when the rate of change of temperature is zero. There are two types of equilibrium solutions: *stable* and *unstable*.\n", "\n", " - A *stable equilibrium* temperature is a solution that the model asymptotes to (moves towards) over time. \n", " - An *unstable equilibrium* temperature is a solution that the model diverges (moves away) from over time. The only time the model will stay at this equilibrium is if it starts *exactly* at the unstable equilibrium temperature. \n", " \n", "We can now incorporate the temperature-dependent albedo we defined above into our time-dependent model from Tutorial 3, to investigate the impact of the ice-albedo feedback on the long-term behavior temperature." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": {}, "executionInfo": { "elapsed": 2, "status": "ok", "timestamp": 1682069593068, "user": { "displayName": "Ohad Zivan", "userId": "06528534325719580785" }, "user_tz": -120 } }, "outputs": [], "source": [ "# create a function to find the new temperature based on the previous using Euler's method.\n", "def step_forward(T, tau, Q, dt):\n", "\n", " # define the heat capacity (calculated in Tutorial 3)\n", " C = 286471954.64 # J m^-2K\n", "\n", " T_new = T + dt / C * (ASR(Q, T) - OLR(tau, T))\n", "\n", " return T_new" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "Let us explore how our model behaves under a variety of initial temperatures. We can use a `for` loop to compare different initial temperatures." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": {}, "executionInfo": { "elapsed": 1690, "status": "ok", "timestamp": 1682069718713, "user": { "displayName": "Ohad Zivan", "userId": "06528534325719580785" }, "user_tz": -120 } }, "outputs": [], "source": [ "dt = 60.0 * 60.0 * 24.0 * 365.0 # time interval, one year expressed in seconds\n", "\n", "fig, ax = plt.subplots()\n", "for init_temp in T: # suite of initial temperatures in K\n", " numtsteps = 40 # number of years to run the model\n", "\n", " # for converting the number of seconds in a year\n", " sec_2_yr = 3.154e7\n", "\n", " # set the initial temperature (initial condition)\n", " T_series = [init_temp]\n", "\n", " # set the initial time to 0\n", " t_series = [0]\n", "\n", " # run the model\n", " for n in range(numtsteps):\n", "\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], tau=tau, Q=Q, dt=dt))\n", "\n", " # make plot\n", " ax.plot(t_series, T_series)\n", "\n", "ax.set_title(\"Temporal Evolution of Temperature\")\n", "ax.set_xlabel(\"Time (years)\")\n", "ax.set_ylabel(\"Temperature (K)\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "### Questions 1.2\n", "\n", "1. How many stable equilibria can you find on the figure above? Estimate their values.\n", "2. What do these values represent on the figure you made in Part 1?\n", "3. There is an unstable equilibrium state within this model. What is it's value?" ] }, { "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_Tutorial4_Solution_05d045f5.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": [ "## Section 1.3: Finding Equilibria Numerically & Determining Convergence or Divergence" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "To verify the equilibrium solutions we identified graphically in the previous section, we can use python to find the exact values (i.e., where the rate of change in temperature is zero). That is find the temperatures that satisfy \n", "\n", "\\begin{align}\n", "0 = ASR-OLR.\n", "\\end{align}\n", "\n", "To aid us, we will use [brentq](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.brentq.html#scipy-optimize-brentq), a *root-finding function* from the `scipy` package." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": {}, "executionInfo": { "elapsed": 2, "status": "ok", "timestamp": 1682069900203, "user": { "displayName": "Ohad Zivan", "userId": "06528534325719580785" }, "user_tz": -120 } }, "outputs": [], "source": [ "# create function to find the forcing at the top of the atmosphere\n", "def Ftoa(T):\n", " return ASR(Q, T) - OLR(tau, T)\n", "\n", "\n", "# brentq() requires a function and two end-points to be input as arguments\n", "# it will look for a root/zero of the function between those end-points\n", "Teq1 = brentq(\n", " Ftoa, 200.0, 240.0\n", ") # these ranges are from the intersections of the graphs of ASR and OLR\n", "Teq2 = brentq(Ftoa, 240.0, 280.0)\n", "Teq3 = brentq(Ftoa, 280.0, 320.0)\n", "\n", "print(Teq1, Teq2, Teq3)" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "To assess the stability of these equilibria, we can plot the difference in $ASR$ and $OSR$. This is the same function `Ftoa` that we calculated in the previous cell, but we will recalculate it below for plotting purposes." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": {}, "executionInfo": { "elapsed": 1877, "status": "ok", "timestamp": 1681699462185, "user": { "displayName": "Jenna Pearson", "userId": "05648130581702734913" }, "user_tz": 420 } }, "outputs": [], "source": [ "# we've already calculated ASR and OLR above\n", "fig, ax = plt.subplots()\n", "F = ASR_vals - OLR_vals\n", "ax.plot(T, F, color=\"k\", linewidth=3)\n", "\n", "# find positive values and fill with red\n", "pos_ind1 = T <= Teq1\n", "ax.fill_between(T[pos_ind1], 0, F[pos_ind1], color=\"red\")\n", "\n", "pos_ind2 = (T >= Teq2) & (T <= Teq3)\n", "ax.fill_between(T[pos_ind2], 0, F[pos_ind2], color=\"red\")\n", "\n", "# find negative values and fill with blue\n", "neg_ind1 = (T >= Teq1) & (T <= Teq2)\n", "ax.fill_between(T[neg_ind1], 0, F[neg_ind1], color=\"blue\")\n", "\n", "neg_ind2 = T >= Teq3\n", "ax.fill_between(T[neg_ind2], 0, F[neg_ind2], color=\"blue\")\n", "\n", "# plot vertical lines/names at equilibrium temperatures\n", "ax.axvline(x=Teq1, color=\"k\", ls=\":\")\n", "ax.axvline(x=Teq2, color=\"k\", ls=\":\")\n", "ax.axvline(x=Teq3, color=\"k\", ls=\":\")\n", "\n", "ax.annotate(\n", " \"$T_{eq1}$\",\n", " xy=(Teq1 - 5, -295),\n", " xytext=(Teq1 - 5, -295),\n", " rotation=90,\n", " annotation_clip=False,\n", ")\n", "ax.annotate(\n", " \"$T_{eq2}$\",\n", " xy=(Teq2 - 5, -295),\n", " xytext=(Teq2 - 5, -295),\n", " rotation=90,\n", " annotation_clip=False,\n", ")\n", "ax.annotate(\n", " \"$T_{eq3}$\",\n", " xy=(Teq3 - 5, -295),\n", " xytext=(Teq3 - 5, -295),\n", " rotation=90,\n", " annotation_clip=False,\n", ")\n", "\n", "# plot arrows/text to show stability of equilibrium points\n", "ax.annotate(\n", " \"\",\n", " xy=(232, -50),\n", " xytext=(200, -50),\n", " arrowprops=dict(facecolor=\"black\", arrowstyle=\"-|>\"),\n", ")\n", "\n", "ax.annotate(\n", " \"\",\n", " xy=(242.5, -50),\n", " xytext=(233, -50),\n", " arrowprops=dict(facecolor=\"black\", arrowstyle=\"<|-\"),\n", ")\n", "\n", "ax.annotate(\n", " \"\",\n", " xy=(305.5, -50),\n", " xytext=(243.5, -50),\n", " arrowprops=dict(facecolor=\"black\", arrowstyle=\"-|>\"),\n", ")\n", "\n", "ax.annotate(\n", " \"\",\n", " xy=(358, -50),\n", " xytext=(307, -50),\n", " arrowprops=dict(facecolor=\"black\", arrowstyle=\"<|-\"),\n", ")\n", "\n", "\n", "ax.annotate(\"convergence\", xy=(358, -170), xytext=(307, -170), rotation=90)\n", "\n", "ax.annotate(\"divergence\", xy=(305.5, -170), xytext=(243.5, -170), rotation=90)\n", "\n", "ax.annotate(\"convergence\", xy=(242.5, -170), xytext=(233, -170), rotation=90)\n", "\n", "\n", "ax.set_xlabel(\"Temperature (K)\")\n", "ax.set_ylabel(\"$ASR-OLR$ (Wm$^{-2}$)\");" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "The red regions represent conditions where the Earth would warm, because the energy absorbed by the Earth system is greater than the energy emitted or reflected back into space. \n", "\n", "The blue regions represent conditions where the Earth would cool, because the outgoing radiation is larger than the absorbed radiation.\n", "\n", "For example, if Earth started at an initial temperature below $T_{\\text{eq1}}$ (in the left red region), it will move to the right on the $x$-axis, towards the $T_{\\text{eq1}}$ equilibrium state. Conversely, if Earth started between $T_{\\text{eq1}}$ and $T_{\\text{eq1}}$ (the left blue region), the temperature would decrease, moving left on the $x$-axis until it reaches $T_{\\text{eq1}}$. Thus $T_{\\text{eq1}}$ is a *stable* equilibrium as the temperature curves will tend to this point after a long time." ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "### Questions 1.3" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "1. Identify the stable and unstable equilibria from this graph. Do these agree with the figure you made in Section 1.2?" ] }, { "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_Tutorial4_Solution_0aa720cf.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_3\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "# Section 2: Changing Insolation" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "## Section 2.1: Effect on the Number Equilibrium Solutions" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "During Day 1 of this week, you learned that insolation (the amount of radiation Earth receives from the sun at the top of the atmosphere) fluctuates with time. Over Earth's history, the insolation has sometimes been lower, and sometimes higher, than the currently observed $340 \\text{ Wm}^{-2}$.\n", "\n", "These insolation changes directly affect the $ASR$, causing Earth to warm or cool depending on whether it receives more or less insolation respectively. To look at the effect that changing insolation has on Earth's equilibrium state(s), we can re-plot $ASR$ as a function of temperature for several different insolation values (including the temperature-dependent albedo), alongside the $OLR$." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": {} }, "outputs": [], "source": [ "# define the observed insolation\n", "Q_vals = [220, 340, 420] # W m^-2\n", "\n", "fig, ax = plt.subplots()\n", "for Q_2 in Q_vals:\n", " # calculate ASR and OLR for different values of T\n", " for tt, temp in enumerate(T):\n", "\n", " ASR_vals[tt] = ASR(Q_2, temp)\n", "\n", " # make plots\n", " ax.plot(T, ASR_vals, label=\"$ASR$ for $Q$ = \" + str(Q_2) + \" Wm$^{-2}$\")\n", "\n", "# note we calculated OLR previously, and it does not depend on Q\n", "ax.plot(T, OLR_vals, label=\"$OLR$\")\n", "\n", "\n", "ax.set_xlabel(\"Temperature (K)\")\n", "ax.set_ylabel(\"Radiative Flux (Wm$^{-2}$)\")\n", "ax.legend()" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "As we increase or decrease the insolation, the number of intersections between $ASR$ and $OLR$ can change! This means the number of equilibrium solutions for our model will also change." ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "### Questions 2.1" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "1. How many stable equilibrium solutions are there when $Q=220 \\text{ Wm}^{-2}$ ? Warm (ice-free) or cold (completely-frozen) state(s)?\n", "2. For $Q=420 \\text{ Wm}^{-2}$ ? Warm or cold equilibrium state(s)?" ] }, { "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_Tutorial4_Solution_523f25ea.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": [ "## Section 2.2: Effect on Equilibrium Temperatures" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "To understand how this effect translates to different equilibrium temperatures of our model over time, we will apply a range of insolation values to our model. Let us first start off with a very cold Earth, at $220 \\text{K}$, and warm the Earth by steadily increasing the insolation above our present-day $340 \\text{ Wm}^{-2}$ value." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": {}, "executionInfo": { "elapsed": 1740, "status": "ok", "timestamp": 1681699463921, "user": { "displayName": "Jenna Pearson", "userId": "05648130581702734913" }, "user_tz": 420 } }, "outputs": [], "source": [ "# these are the values of insolation we will use\n", "insolation_vals = np.arange(340, 500, 3)\n", "\n", "# initial temperature we will use\n", "init_temp = 220 # K\n", "\n", "fig, ax = plt.subplots()\n", "\n", "for i, insolation in enumerate(insolation_vals): # suite of initial temperatures in K\n", "\n", " numtsteps = 100 # number of years to run the model\n", "\n", " # for converting the number of seconds in a year\n", " sec_2_yr = 3.154e7\n", "\n", " # set the initial temperature (initial condition)\n", " T_series = [init_temp]\n", "\n", " # set the initial time to 0\n", " t_series = [0]\n", "\n", " # run the model\n", " for n in range(numtsteps):\n", "\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], tau=tau, Q=insolation, dt=dt))\n", "\n", " # make plot\n", " colors = plt.cm.coolwarm(np.linspace(0, 1, insolation_vals.shape[0]))\n", " if (\n", " insolation == 385\n", " ): # This is just to highlight a particularly interesting insolation value\n", " ax.plot(t_series, T_series, color=colors[i], linestyle=\"dashed\")\n", " else:\n", " ax.plot(t_series, T_series, color=colors[i])\n", "\n", "ax.set_ylabel(\"Temperature (K)\")\n", "ax.set_xlabel(\"Time (years)\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "### Questions 2.2: Climate Connection" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "1. Noting the dashed blue lines, at approximately what temperature do you note a rapid transition from cold to warm equilibrium states? How do these compare to your equation for albedo?\n", "2. How would you interpret the rapid transition in equilibrium temperatures with changing insolation (the big gap in the figure) using the $ASR$ & $OLR$ vs. temperature plot that you made in Section 2.1?\n", "3. How does the time-varying behavior of the reddest (warm-state) lines relate to the ice-albedo feedback?" ] }, { "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_Tutorial4_Solution_f13e9cfb.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_2\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "# Summary\n", "In this tutorial, you learned about stable and unstable equilibria, identifying them from graphs and precisely calculating them. You also incorporated an ice-albedo feedback into your model to observe its effects on equilibrium solutions under varying insolation." ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "# Resources" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "Useful links:\n", "\n", "- [The Climate Laboratory](https://brian-rose.github.io/ClimateLaboratoryBook/) by Brian E. J. Rose\n", "- [Climate feedbacks, Annex VII, IPCC AR6](https://www.ipcc.ch/report/ar6/wg1/downloads/report/IPCC_AR6_WGI_AnnexVII.pdf)\n", "- [Scipy optimize brentq()](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.brentq.html#scipy-optimize-brentq)." ] } ], "metadata": { "colab": { "collapsed_sections": [], "include_colab_link": true, "name": "W1D5_Tutorial4", "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 }