\n",
"## Click here for a description of the plot

\n",
"Heat content that is contained in the water mass from the surface to the respective depth, for all depths calculated with a cumulative sum. \n",
"Hence, the overall global ocean heat, integrated from 6000m to the surface, is approximately 0.02 * 10^6 ZJ.\n",
"\n",
"***\n",
"\n",
"

"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"You can see that much of the ocean's heat is concentrated in the upper ocean (where the line is steep), with less heat stored in the deepest ocean regions (where the line plateaus). At first glance, this seems consistent with the zonal mean plot you plotted earlier in the tutorial, where the upper ocean tends to be warmer than deeper waters. However, in the integral equation above, $\\theta$ is not the only depth-dependent term. The global ocean area ($A$) also varies with depth, with the area of the global ocean decreasing with depth until only a few deep trenches contain water at the greatest ocean depths.\n",
"\n",
"Let's explore whether the ocean heat content plot we just created is driven by temperature variations or global ocean area variations with depth. One way to do this is to calculate and plot an integral of the global ocean area between each depth and the surface (i.e., the volume of the ocean above a each depth): $\\text{Volume}(z) = \\iiint_{-z_1}^0 dz dA$.\n",
"\n",
"If the volume as a function of depth looks similar to the heat content plot above, it would suggest that the smaller heat content of the deeper ocean (i.e., the plateau at large depths) is caused by the relatively small volume of water contained at these depths, rather than the vertical variations in temperature.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"area_of_ocean = (\n",
" (subset_theta * subset_theta.dA / subset_theta).sum(\"latitude\").sum(\"longitude\")\n",
") # we take an area integral first at each depth level\n",
"\n",
"fig, ax = plt.subplots()\n",
"ax.plot(-subset_theta.Zu, (area_of_ocean * subset_theta.dZ).cumsum())\n",
"\n",
"ax.grid(True)\n",
"ax.set_xlabel(\"Depth (m)\")\n",
"ax.set_ylabel(\"Volume of the global ocean above this depth (m$^3$)\")\n",
"ax.set_title(\"Global ocean volume above each depth\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Questions 1\n",
"\n",
"1. Based on the last two plots, are depth-variations in ocean heat content primarily due to vertical changes in the temperature or area of the ocean?\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D2_Ocean-AtmosphereReanalysis/solutions/W1D2_Tutorial6_Solution_d0a20187.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\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Section 2: Changes in Ocean Heat Content"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"In this section you will examine how the total (i.e., full-depth) heat content of the ocean has changed over time. You will also explore heat content changes within two specific upper-ocean layers: one above 700 m depth and another above 2000 m depth$^*$. By analyzing these near surface layers, you will identify whether changes in the ocean's heat content are evenly distributed through the water column.\n",
"\n",
"$^*$*Note: technically the grid of the data means you will be looking above 677 m and 1997 m respectively*"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# this cell may take a while to run!\n",
"rho = 1026 # kg/m^3\n",
"c_p = 3990 # J/(kg K)\n",
"# initialize lists to store resulting values\n",
"global_heat, years_to_plot, global_heat_upper2000, global_heat_upper700 = [], [], [], []\n",
"# for every year of the data set\n",
"for year in theta_annual.year:\n",
" # fill the years of interest list\n",
" years_to_plot.append(int(year))\n",
" # select a year and calculate global heat content over the whole ocean depth, ..\n",
" subset_theta_year = theta_annual.sel(year=int(year))\n",
" global_heat.append(\n",
" float(\n",
" rho\n",
" * c_p\n",
" * (subset_theta_year * subset_theta_year.dZ * subset_theta_year.dA)\n",
" .sum(\"Z\")\n",
" .sum(\"latitude\")\n",
" .sum(\"longitude\")\n",
" )\n",
" )\n",
" # .., the upper 2000m\n",
" global_heat_upper2000.append(\n",
" float(\n",
" rho\n",
" * c_p\n",
" * (\n",
" (\n",
" subset_theta_year.where(subset_theta_year.Zu > -2000)\n",
" * subset_theta_year.dZ\n",
" * subset_theta_year.dA\n",
" )\n",
" .sum(\"Z\")\n",
" .sum(\"latitude\")\n",
" .sum(\"longitude\")\n",
" )\n",
" )\n",
" )\n",
" # .., and the upper 700m\n",
" global_heat_upper700.append(\n",
" float(\n",
" rho\n",
" * c_p\n",
" * (\n",
" (\n",
" subset_theta_year.where(subset_theta_year.Zu > -700)\n",
" * subset_theta_year.dZ\n",
" * subset_theta_year.dA\n",
" )\n",
" .sum(\"Z\")\n",
" .sum(\"latitude\")\n",
" .sum(\"longitude\")\n",
" )\n",
" )\n",
" )\n",
"\n",
"# we now have lists, and list don't support math operations (-)\n",
"# we also divide the values by 10**21 to make them easier to read. Unit is therefore ZJ (zetta joule)\n",
"\n",
"heat_anom_fulldepth = [\n",
" (heat - global_heat[0]) / 10**21 for heat in global_heat\n",
"] # subtract year 1992 to get anomaly\n",
"heat_anom_upper2000 = [\n",
" (heat - global_heat_upper2000[0]) / 10**21 for heat in global_heat_upper2000\n",
"] # subtract year 1992 to get anomaly\n",
"heat_anom_upper700 = [\n",
" (heat - global_heat_upper700[0]) / 10**21 for heat in global_heat_upper700\n",
"] # subtract year 1992 to get anomaly\n",
"heat_anom_upper2000_700 = [\n",
" a - b for a, b in zip(heat_anom_upper2000, heat_anom_upper700)\n",
"] # difference series between 2000 m to 700 m\n",
"heat_anom_upperfulldepth_2000 = [\n",
" a - b for a, b in zip(heat_anom_fulldepth, heat_anom_upper2000)\n",
"] # difference series between full depth to 2000"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"fig, ax = plt.subplots()\n",
"\n",
"ax.plot(years_to_plot, heat_anom_fulldepth, \"k--\")\n",
"ax.plot(years_to_plot, heat_anom_upper700)\n",
"ax.plot(years_to_plot, heat_anom_upper2000_700)\n",
"ax.plot(years_to_plot, heat_anom_upperfulldepth_2000)\n",
"\n",
"# aesthetics\n",
"ax.grid(True)\n",
"ax.set_xlabel(\"Time\")\n",
"ax.set_ylabel(\"Heat content change (ZJ)\")\n",
"ax.legend(\n",
" [\n",
" \"Full depth\",\n",
" \"Surface to 700 meters depth\",\n",
" \"700 to 2000 meters depth\",\n",
" \"Below 2000 meters depth\",\n",
" ]\n",
")\n",
"ax.set_title(\"Change in ocean heat content over time\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Questions 2\n",
"\n",
"The volume of the ocean in the model that was applied to create the reanalysis product does not change over time. Thus the *changes* in ocean heat content that you just calculated are caused by changes in the ocean's temperature. Most of the ocean's warming (heat gain) has been within the upper ocean (shallower than 700 m). The deeper ocean has also warmed, but not as substantially as near-surface waters.\n",
"\n",
"1. Based on this graph, what percentage of the ocean's heat gain since 1992 is contained within the top 2000 meters?\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"[*Click for solution*](https://github.com/neuromatch/climate-course-content/tree/main/tutorials/W1D2_Ocean-AtmosphereReanalysis/solutions/W1D2_Tutorial6_Solution_04fceaff.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\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Section 3: Spatial Distribution of Ocean Heat Content"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"You just saw that the ocean heat increase is concentrated near the ocean surface. **Now you will explore where that heat is stored as a function of latitude and longitude**. You can do this by creating a global map of ocean heat content in the upper 700 m of the ocean - which is essentially the same integral as above without the horizontal area integral: $\\int_{-700m}^0 c_p\\rho_0\\theta(x,y,z) dz$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# first let's plot where heat is stored in the mean\n",
"fig, ax = plt.subplots(\n",
" subplot_kw={\"projection\": ccrs.PlateCarree()}, dpi=100\n",
") # this is from cartopy https://rabernat.github.io/research_computing_2018/maps-with-cartopy.html\n",
"\n",
"p = (\n",
" (\n",
" (rho * c_p * subset_theta.where(-subset_theta.Zu < 700) * subset_theta.dZ).sum(\n",
" \"Z\"\n",
" )\n",
" )\n",
").plot(\n",
" vmin=7e11,\n",
" vmax=8.15e11,\n",
" cmap=cmo.cm.thermal,\n",
" cbar_kwargs={\n",
" \"shrink\": 0.75,\n",
" \"orientation\": \"horizontal\",\n",
" \"extend\": \"both\",\n",
" \"pad\": 0.15,\n",
" \"label\": \"J/m$^2$\",\n",
" },\n",
" ax=ax,\n",
")\n",
"ax.coastlines(color=\"grey\", lw=0.5)\n",
"ax.set_xticks([-180, -120, -60, 0, 60, 120, 180], crs=ccrs.PlateCarree())\n",
"ax.set_yticks([-90, -60, -30, 0, 30, 60, 90], crs=ccrs.PlateCarree())\n",
"lon_formatter = LongitudeFormatter(zero_direction_label=True)\n",
"lat_formatter = LatitudeFormatter()\n",
"ax.add_feature(cfeature.LAND, zorder=100, edgecolor=\"k\")\n",
"ax.set_title(\n",
" \"Ocean Heat Content of top 700 m per unit area, mean of 1992 to 1994\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"The lower latitude ocean contains more heat than the higher latitudes. This finding is consistent with your previous plot of warmer waters near the Equator during this tutorial. \n",
"\n",
"*Note: the color scale of this figure was chosen to emphasize latitudinal differences in ocean heat conent. As a result, some regions with shallow water depth display as black on the plot due to their relatively low column-integrated ocean heat content ($<7 \\times 10^{11} J m^{-2}$). **These black regions do not have zero ocean heat content.***\n",
"\n",
"Now let's explore the spatial pattern of (full-depth) ocean heat content *rate of change* between 1992 and 2016."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# we already defined an object that's the mean over years 1992 to 1994 (subset_theta)\n",
"# now define an object that's the mean over 2014 to 2016\n",
"subset_theta_future = theta_annual.sel(year=slice(\"2014\", \"2016\")).mean(\"year\")\n",
"\n",
"length_of_time_period = 24 * 60 * 60 * 365 * (2015 - 1993) # calculate length in seconds\n",
"\n",
"# difference between future and present heat content over whole depth range\n",
"full_depth_heat_content_change = (rho * c_p * subset_theta_future * subset_theta_future.dZ).sum(\"Z\")\n",
"- (rho * c_p * subset_theta * subset_theta.dZ).sum(\"Z\")\n",
"# difference between future and present heat content for upper 700m layer\n",
"upper_700m_heat_content_change = (\n",
" rho\n",
" * c_p\n",
" * subset_theta_future.where(-subset_theta.Zu < 700)\n",
" * subset_theta_future.dZ\n",
").sum(\"Z\") - (\n",
" rho * c_p * subset_theta.where(-subset_theta.Zu < 700) * subset_theta.dZ\n",
").sum(\n",
" \"Z\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# plot 2 maps to compare changes in heat content in those two layers\n",
"fig, ax = plt.subplots(\n",
" 1, 2, subplot_kw={\"projection\": ccrs.PlateCarree()}, figsize=(16,6), dpi=100\n",
") # this is from cartopy https://rabernat.github.io/research_computing_2018/maps-with-cartopy.html\n",
"# full depth plot\n",
"(full_depth_heat_content_change / length_of_time_period).plot(\n",
" ax=ax[0],\n",
" vmin=-10,\n",
" vmax=10,\n",
" cmap=cmo.cm.balance,\n",
" cbar_kwargs={\n",
" \"shrink\": 0.75,\n",
" \"orientation\": \"horizontal\",\n",
" \"extend\": \"both\",\n",
" \"pad\": 0.15,\n",
" \"label\": \"(J m$^{-2}$year$^{-1}$)\",\n",
" },\n",
")\n",
"# upper layer plot\n",
"(upper_700m_heat_content_change / length_of_time_period).plot(\n",
" ax=ax[1],\n",
" vmin=-10,\n",
" vmax=10,\n",
" cmap=cmo.cm.balance,\n",
" cbar_kwargs={\n",
" \"shrink\": 0.75,\n",
" \"orientation\": \"horizontal\",\n",
" \"extend\": \"both\",\n",
" \"pad\": 0.15,\n",
" \"label\": \"(J m$^{-2}$year$^{-1}$)\",\n",
" },\n",
")\n",
"ax[0].coastlines(color=\"grey\", lw=0.5)\n",
"ax[0].set_xticks([-180, -120, -60, 0, 60, 120, 180], crs=ccrs.PlateCarree())\n",
"ax[0].set_yticks([-90, -60, -30, 0, 30, 60, 90], crs=ccrs.PlateCarree())\n",
"lon_formatter = LongitudeFormatter(zero_direction_label=True)\n",
"lat_formatter = LatitudeFormatter()\n",
"ax[0].add_feature(cfeature.LAND, zorder=100, edgecolor=\"k\")\n",
"ax[0].set_title(\n",
" \"Rate of change in full-depth ocean heat content \\n (2014-2016 minus 1992-1994)\"\n",
")\n",
"ax[1].coastlines(color=\"grey\", lw=0.5)\n",
"ax[1].set_xticks([-180, -120, -60, 0, 60, 120, 180], crs=ccrs.PlateCarree())\n",
"ax[1].set_yticks([-90, -60, -30, 0, 30, 60, 90], crs=ccrs.PlateCarree())\n",
"ax[0].set_ylabel(\"Latitude\")\n",
"ax[1].set_ylabel(\"\")\n",
"ax[0].set_xlabel(\"Longitude\")\n",
"ax[1].set_xlabel(\"Longitude\")\n",
"lon_formatter = LongitudeFormatter(zero_direction_label=True)\n",
"lat_formatter = LatitudeFormatter()\n",
"ax[1].add_feature(cfeature.LAND, zorder=100, edgecolor=\"k\")\n",
"ax[1].set_title(\n",
" \"Rate of change in upper 700 m ocean heat content \\n (2014-2016 minus 1992-1994)\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"With these plots, you demonstrated that ocean heat gain is not evenly distributed across space. When comparing the two plots, you once again see that the upper ocean contains a large fraction of the warming (recall that equatorial regions contribute more to the global mean than high-latitude regions because of their relatively large area)."
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Summary\n",
"\n",
"In this tutorial, you have quantified the spatial patterns and temporal changes of the ocean's heat content. You showed that the upper layers of the ocean contain most of the ocean's heat content, due to their relatively large area (and hence volume) compared to the deepest ocean layers. These upper layers also experience a disproportionately large fraction of the ocean warming that has been observed in recent decades. You also found that heat content distribution varies by latitude and longitude, and is typically greater in the lower latitudes, and the ocean's heat gain over time is not uniformly distributed across different oceanic regions."
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Resources\n",
"\n",
"Data for this tutorial can be accessed [here](https://www.ecco-group.org/)."
]
}
],
"metadata": {
"colab": {
"collapsed_sections": [],
"include_colab_link": true,
"name": "W1D2_Tutorial6",
"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
}