6. A Brief Review of Radiation#
This notebook is part of The Climate Laboratory by Brian E. J. Rose, University at Albany.
1. Emission temperature and lapse rates#
Planetary energy balance is the foundation for all climate modeling. So far we have expressed this through a globally averaged budget
and we have written the OLR in terms of an emission temperature \(T_e\) where by definition
Using values from the observed planetary energy budget, we found that \(T_e = 255\) K
The emission temperature of the planet is thus about 33 K colder than the mean surface temperature (288 K).
Where in the atmosphere do we find \(T = T_e = 255\) K?#
That’s about -18ºC.
Let’s plot global, annual average observed air temperature from NCEP reanalysis data.
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import xarray as xr
## The NOAA ESRL server is shutdown! January 2019
ncep_url = "http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/ncep.reanalysis.derived/"
ncep_air = xr.open_dataset( ncep_url + "pressure/air.mon.1981-2010.ltm.nc",
use_cftime=True)
#url = 'http://apdrc.soest.hawaii.edu:80/dods/public_data/Reanalysis_Data/NCEP/NCEP/clima/pressure/air'
#air = xr.open_dataset(url)
## The name of the vertical axis is different than the NOAA ESRL version..
#ncep_air = air.rename({'lev': 'level'})
ncep_air
<xarray.Dataset> Dimensions: (lat: 73, level: 17, lon: 144, nbnds: 2, time: 12) Coordinates: * level (level) float32 1e+03 925.0 850.0 ... 30.0 20.0 10.0 * lon (lon) float32 0.0 2.5 5.0 7.5 ... 352.5 355.0 357.5 * time (time) object 0001-01-01 00:00:00 ... 0001-12-01 00:0... * lat (lat) float32 90.0 87.5 85.0 82.5 ... -85.0 -87.5 -90.0 Dimensions without coordinates: nbnds Data variables: climatology_bounds (time, nbnds) object 1981-01-01 00:00:00 ... 2010-12-... air (time, level, lat, lon) float32 ... valid_yr_count (time, level, lat, lon) float32 ... Attributes: description: Data from NCEP initialized reanalysis (4... platform: Model Conventions: COARDS not_missing_threshold_percent: minimum 3% values input to have non-missi... history: Created 2011/07/12 by doMonthLTM\nConvert... title: monthly ltm air from the NCEP Reanalysis dataset_title: NCEP-NCAR Reanalysis 1 References: http://www.psl.noaa.gov/data/gridded/data...
- lat: 73
- level: 17
- lon: 144
- nbnds: 2
- time: 12
- level(level)float321e+03 925.0 850.0 ... 20.0 10.0
- units :
- millibar
- long_name :
- Level
- positive :
- down
- GRIB_id :
- 100
- GRIB_name :
- hPa
- actual_range :
- [1000. 10.]
- axis :
- Z
array([1000., 925., 850., 700., 600., 500., 400., 300., 250., 200., 150., 100., 70., 50., 30., 20., 10.], dtype=float32)
- lon(lon)float320.0 2.5 5.0 ... 352.5 355.0 357.5
- units :
- degrees_east
- long_name :
- Longitude
- actual_range :
- [ 0. 357.5]
- standard_name :
- longitude
- axis :
- X
array([ 0. , 2.5, 5. , 7.5, 10. , 12.5, 15. , 17.5, 20. , 22.5, 25. , 27.5, 30. , 32.5, 35. , 37.5, 40. , 42.5, 45. , 47.5, 50. , 52.5, 55. , 57.5, 60. , 62.5, 65. , 67.5, 70. , 72.5, 75. , 77.5, 80. , 82.5, 85. , 87.5, 90. , 92.5, 95. , 97.5, 100. , 102.5, 105. , 107.5, 110. , 112.5, 115. , 117.5, 120. , 122.5, 125. , 127.5, 130. , 132.5, 135. , 137.5, 140. , 142.5, 145. , 147.5, 150. , 152.5, 155. , 157.5, 160. , 162.5, 165. , 167.5, 170. , 172.5, 175. , 177.5, 180. , 182.5, 185. , 187.5, 190. , 192.5, 195. , 197.5, 200. , 202.5, 205. , 207.5, 210. , 212.5, 215. , 217.5, 220. , 222.5, 225. , 227.5, 230. , 232.5, 235. , 237.5, 240. , 242.5, 245. , 247.5, 250. , 252.5, 255. , 257.5, 260. , 262.5, 265. , 267.5, 270. , 272.5, 275. , 277.5, 280. , 282.5, 285. , 287.5, 290. , 292.5, 295. , 297.5, 300. , 302.5, 305. , 307.5, 310. , 312.5, 315. , 317.5, 320. , 322.5, 325. , 327.5, 330. , 332.5, 335. , 337.5, 340. , 342.5, 345. , 347.5, 350. , 352.5, 355. , 357.5], dtype=float32)
- time(time)object0001-01-01 00:00:00 ... 0001-12-...
- long_name :
- Time
- delta_t :
- 0000-01-00 00:00:00
- avg_period :
- 0030-00-00 00:00:00
- prev_avg_period :
- 0000-01-00 00:00:00
- standard_name :
- time
- axis :
- T
- climatology :
- climatology_bounds
- climo_period :
- 1981/01/01 - 2010/12/31
- interpreted_actual_range :
- 0001/01/01 00:00:00 - 0001/12/01 00:00:00
- actual_range :
- [-657073. -656739.]
array([cftime.DatetimeGregorian(1, 1, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 2, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 3, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 4, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 5, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 6, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 7, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 8, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 9, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 10, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 11, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(1, 12, 1, 0, 0, 0, 0)], dtype=object)
- lat(lat)float3290.0 87.5 85.0 ... -87.5 -90.0
- units :
- degrees_north
- actual_range :
- [ 90. -90.]
- long_name :
- Latitude
- standard_name :
- latitude
- axis :
- Y
array([ 90. , 87.5, 85. , 82.5, 80. , 77.5, 75. , 72.5, 70. , 67.5, 65. , 62.5, 60. , 57.5, 55. , 52.5, 50. , 47.5, 45. , 42.5, 40. , 37.5, 35. , 32.5, 30. , 27.5, 25. , 22.5, 20. , 17.5, 15. , 12.5, 10. , 7.5, 5. , 2.5, 0. , -2.5, -5. , -7.5, -10. , -12.5, -15. , -17.5, -20. , -22.5, -25. , -27.5, -30. , -32.5, -35. , -37.5, -40. , -42.5, -45. , -47.5, -50. , -52.5, -55. , -57.5, -60. , -62.5, -65. , -67.5, -70. , -72.5, -75. , -77.5, -80. , -82.5, -85. , -87.5, -90. ], dtype=float32)
- climatology_bounds(time, nbnds)object...
- long_name :
- Climate Time Boundaries
array([[cftime.DatetimeGregorian(1981, 1, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 1, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 2, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 2, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 3, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 3, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 4, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 4, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 5, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 5, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 6, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 6, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 7, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 7, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 8, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 8, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 9, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 9, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 10, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 10, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 11, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 11, 1, 0, 0, 0, 0)], [cftime.DatetimeGregorian(1981, 12, 1, 0, 0, 0, 0), cftime.DatetimeGregorian(2010, 12, 1, 0, 0, 0, 0)]], dtype=object)
- air(time, level, lat, lon)float32...
- long_name :
- Monthly Long Term Mean of Air temperature
- units :
- degC
- precision :
- 2
- var_desc :
- Air Temperature
- level_desc :
- Multiple levels
- statistic :
- Long Term Mean
- parent_stat :
- Mean
- valid_range :
- [-200. 300.]
- actual_range :
- [-89.722336 41.616005]
- dataset :
- NCEP Reanalysis Derived Products
- _ChunkSizes :
- [ 1 1 73 144]
[2144448 values with dtype=float32]
- valid_yr_count(time, level, lat, lon)float32...
- long_name :
- count of non-missing values used in mean
- _ChunkSizes :
- [ 1 1 73 144]
[2144448 values with dtype=float32]
- description :
- Data from NCEP initialized reanalysis (4x/day). These are interpolated to pressure surfaces from model (sigma) surfaces.
- platform :
- Model
- Conventions :
- COARDS
- not_missing_threshold_percent :
- minimum 3% values input to have non-missing output value
- history :
- Created 2011/07/12 by doMonthLTM Converted to chunked, deflated non-packed NetCDF4 2014/09
- title :
- monthly ltm air from the NCEP Reanalysis
- dataset_title :
- NCEP-NCAR Reanalysis 1
- References :
- http://www.psl.noaa.gov/data/gridded/data.ncep.reanalysis.derived.html
# Take global, annual average and convert to Kelvin
coslat = np.cos(np.deg2rad(ncep_air.lat))
weight = coslat / coslat.mean(dim='lat')
Tglobal = (ncep_air.air * weight).mean(dim=('lat','lon','time'))
Tglobal
<xarray.DataArray (level: 17)> array([ 15.179084 , 11.207003 , 7.8383274 , 0.21994135, -6.4483433 , -14.888848 , -25.570469 , -39.36969 , -46.797905 , -53.652245 , -60.56356 , -67.006065 , -65.53293 , -61.48664 , -55.853584 , -51.593952 , -43.21999 ], dtype=float32) Coordinates: * level (level) float32 1e+03 925.0 850.0 700.0 ... 50.0 30.0 20.0 10.0
- level: 17
- 15.18 11.21 7.838 0.2199 -6.448 ... -65.53 -61.49 -55.85 -51.59 -43.22
array([ 15.179084 , 11.207003 , 7.8383274 , 0.21994135, -6.4483433 , -14.888848 , -25.570469 , -39.36969 , -46.797905 , -53.652245 , -60.56356 , -67.006065 , -65.53293 , -61.48664 , -55.853584 , -51.593952 , -43.21999 ], dtype=float32)
- level(level)float321e+03 925.0 850.0 ... 20.0 10.0
- units :
- millibar
- long_name :
- Level
- positive :
- down
- GRIB_id :
- 100
- GRIB_name :
- hPa
- actual_range :
- [1000. 10.]
- axis :
- Z
array([1000., 925., 850., 700., 600., 500., 400., 300., 250., 200., 150., 100., 70., 50., 30., 20., 10.], dtype=float32)
# a "quick and dirty" visualization of the data
Tglobal.plot()
[<matplotlib.lines.Line2D at 0x15b6aad60>]
Let’s make a better plot.
Here we’re going to use a package called metpy
to automate plotting this temperature profile in a way that’s more familiar to meteorologists: a so-called skew-T plot.
from metpy.plots import SkewT
fig = plt.figure(figsize=(9, 9))
skew = SkewT(fig, rotation=30)
skew.plot(Tglobal.level, Tglobal, color='black', linestyle='-', linewidth=2, label='Observations')
skew.ax.set_ylim(1050, 10)
skew.ax.set_xlim(-75, 45)
# Add the relevant special lines
skew.plot_dry_adiabats(linewidth=0.5)
skew.plot_moist_adiabats(linewidth=0.5)
#skew.plot_mixing_lines()
skew.ax.legend()
skew.ax.set_title('Global, annual mean sounding from NCEP Reanalysis',
fontsize = 16)
Text(0.5, 1.0, 'Global, annual mean sounding from NCEP Reanalysis')
Note that surface temperature in global mean is indeed about 288 K or 15ºC as we keep saying.
So where do we find temperature \(T_e=255\) K or -18ºC?
Actually in mid-troposphere, near 500 hPa or about 5 km height.
We can infer that much of the outgoing longwave radiation actually originates far above the surface.
Recall that our observed global energy budget diagram shows 217 out of 239 W m\(^{-2}\) total OLR emitted by the atmosphere and clouds, only 22 W m\(^{-2}\) directly from the surface.
This is due to the greenhouse effect.
So far we have dealt with the greenhouse in a very artificial way in our energy balance model by simply assuming
i.e., the OLR is reduced by a constant factor from the value it would have if the Earth emitted as a blackbody at the surface temperature.
Now it’s time to start thinking a bit more about how the radiative transfer process actually occurs in the atmosphere, and how to model it.
2. Solar Radiation#
Let’s plot a spectrum of solar radiation.
For Python details, click to expand the code blocks (or see the code in the notebook).
# Using pre-defined code for the Planck function from the climlab package
from climlab.utils.thermo import Planck_wavelength
# approximate emission temperature of the sun in Kelvin
Tsun = 5780.
# boundaries of visible region in nanometers
UVbound = 390.
IRbound = 700.
# array of wavelengths
wavelength_nm = np.linspace(10., 3500., 400)
to_meters = 1E-9 # conversion factor
label_size = 16
fig, ax = plt.subplots(figsize=(14,7))
ax.plot(wavelength_nm,
Planck_wavelength(wavelength_nm * to_meters, Tsun))
ax.grid()
ax.set_xlabel('Wavelength (nm)', fontsize=label_size)
ax.set_ylabel('Spectral radiance (W sr$^{-1}$ m$^{-3}$)', fontsize=label_size)
# Mask out points outside of this range
wavelength_vis = np.ma.masked_outside(wavelength_nm, UVbound, IRbound)
# Shade the visible region
ax.fill_between(wavelength_vis, Planck_wavelength(wavelength_vis * to_meters, Tsun))
title = 'Blackbody emission curve for the sun (T = {:.0f} K)'.format(Tsun)
ax.set_title(title, fontsize=label_size);
ax.text(280, 0.8E13, 'Ultraviolet', rotation='vertical', fontsize=12)
ax.text(500, 0.8E13, 'Visible', rotation='vertical', fontsize=16, color='w')
ax.text(800, 0.8E13, 'Infrared', rotation='vertical', fontsize=12);
Spectrum peaks in the visible range
most energy at these wavelength.
No coincidence that our eyes are sensitive to this range of wavelengths!
Longer wavelengths called “infrared”, shorter wavelengths called “ultraviolet”.
The shape of the spectrum is a fundamental characteristic of radiative emissions (think about the color of burning coals in a fire – cooler = red, hotter = white)
Theory and experiments tell us that both the total flux of emitted radiation, and the wavelength of maximum emission, depend only on the temperature of the source!
The theoretical spectrum was worked out by Max Planck and is therefore known as the “Planck” spectrum (or simply blackbody spectrum).
fig, ax = plt.subplots(figsize=(14,7))
wavelength_um = wavelength_nm / 1000
for T in [24000,12000,6000,3000]:
ax.plot(wavelength_um,
(Planck_wavelength(wavelength_nm * to_meters, T) / T**4),
label=str(T) + ' K')
ax.legend(fontsize=label_size)
ax.set_xlabel('Wavelength (um)', fontsize=label_size)
ax.set_ylabel('Normalized spectral radiance (W sr$^{-1}$ m$^{-3}$ K$^{-4}$)', fontsize=label_size)
ax.set_title("Normalized blackbody emission spectra $T^{-4} B_{\lambda}$ for different temperatures");
Going from cool to warm:
total emission increases
maximum emission occurs at shorter wavelengths.
The integral of these curves over all wavelengths gives us our familiar \(\sigma T^4\)
Mathematically it turns out that
(known as Wien’s displacement law).
By fitting the observed solar emission to a blackbody curve, we can deduce that the emission temperature of the sun is about 6000 K.
Knowing this, and knowing that the solar spectrum peaks at 0.6 micrometers, we can calculate the wavelength of maximum terrestrial radiation as
This is in the far-infrared part of the spectrum.
3. Terrestrial Radiation and absorption spectra#
Terrestrial versus solar wavelengths#
Now let’s look at normalized blackbody curves for Sun and Earth:
fig, ax = plt.subplots(figsize=(14,7))
wavelength_um = np.linspace(0.1, 200, 10000)
wavelength_meters = wavelength_um / 1E6
for T in [6000, 255]:
ax.semilogx(wavelength_um,
(Planck_wavelength(wavelength_meters, T) / T**4 * wavelength_meters),
label=str(T) + ' K')
ax.legend(fontsize=label_size)
ax.set_xlabel('Wavelength (um)', fontsize=label_size)
ax.set_ylabel('Normalized spectral radiance (W sr$^{-1}$ m$^{-2}$ K$^{-4}$)', fontsize=label_size)
ax.set_title("Normalized blackbody emission spectra $T^{-4} \lambda B_{\lambda}$ for the sun ($T_e = 6000$ K) and Earth ($T_e = 255$ K)",
fontsize=label_size);
There is essentially no overlap between the two spectra.
This is the fundamental reason we can discuss the solar “shortwave” and terrestrial “longwave” radiation as two distinct phenomena.
In reality all radiation exists on a continuum of different wavelengths. But in climate science we can get a long way by thinking in terms of a very simple “two-stream” approximation (short and longwave). We’ve already been doing this throughout the course so far!
Atmospheric absorption spectra#
Now look at the atmospheric absorption spectra.
(fraction of radiation at each wavelength that is absorbed on a single vertical path through the atmosphere)
Figure reproduced from Marshall and Plumb (2007): Atmosphere, Ocean, and Climate Dynamics [Marshall and Plumb, 2007]
Atmosphere is almost completely transparent in the visible range, right at the peak of the solar spectrum
Atmosphere is very opaque in the UV
Opacity across the IR spectrum is highly variable!
Look at the gases associated with various absorption features:
Main players include H\(_2\)O, CO\(_2\), N\(_2\)O, O\(_2\).
Compare to major constituents of atmosphere, in decreasing order:
78% N\(_2\)
21% O\(_2\)
1% Ar
H\(_2\)O (variable)
The dominant constituent gases N\(_2\) and O\(_2\) are nearly completely transparent across the entire spectrum (there are O\(_2\) absorption features in far UV, but little energy at these wavelengths).
The greenhouse effect mostly involves trace constituents:
O\(_3\) = 500 ppb
N\(_2\)O = 310 ppb
CO\(_2\) = 400 ppm (but rapidly increasing!)
CH\(_4\) = 1.7 ppm
Note that most of these are tri-atomic molecules! There are fundamental reasons for this: these molecules have modes of rotational and vibration that are easily excited at IR wavelengths. See courses in radiative transfer!
Credits#
This notebook is part of The Climate Laboratory, an open-source textbook developed and maintained by Brian E. J. Rose, University at Albany.
It is licensed for free and open consumption under the Creative Commons Attribution 4.0 International (CC BY 4.0) license.
Development of these notes and the climlab software is partially supported by the National Science Foundation under award AGS-1455071 to Brian Rose. Any opinions, findings, conclusions or recommendations expressed here are mine and do not necessarily reflect the views of the National Science Foundation.