Absorption: Spectral Line Shapes
The purpose of this tutorial is to demonstrate how to compute line-shapes using the vSmartMOM Julia package. Here, we focus on rotational-vibrational transition lines, what processes determine line-shapes, and how the line-shapes depend on pressure and temperature.
Basic Tools
We are making use of the HITRAN database, see a list of tutorials here.
There are other spectroscopic linelists, but we are focusing on HITRAN only here. In addition, there are more complex line-shapes that we are not treating here, including collisional narrowing and line-mixing, collision-induced absorption (CIA). In the future, these might be added to vSmartMOM.
Line-shape:
If we have a line-strength S (in cm
where
There are several processes that affect the shape and width of
Doppler Broadening
Doppler broadening is caused by a simple doppler shift of emitted and absorbed frequencies, caused by the relative velocities of the molecules along the line of sight. A doppler shifted apparent frequency from the centroid frequency
where
Let us take a simple example with the satellite flying at 7km/s and either staring into the flight direction (technically, it wouldn't see the atmosphere then, but let's ignore this) or looking into the back:
# Speed of light (in m/s)
c = 299792458.0
# Relative velocity (in m/s)
vᵣ = 7000.0
# Center wavenumber (say 1600nm, which is 1e7/1600=6250 cm$^{-1}$)
v₀ = 6250.0
# Doppler shift:
Δ_ν = ( v₀ * vᵣ) / c
# Just writing out doppler shift in wavenumbers and wavelengths
println("Doppler shift = $Δ_ν cm-1")
println("Doppler shift = $(1e7/(v₀-Δ_ν)-1e7/v₀) nm")Random movements of molecules in gases lead to doppler broadening effects
In the one-dimensional case – say along the x-axis –, the speed of molecules is distributed according to the Maxwell-Boltzmann distribution:
where
We can thus define a Doppler width
which yields the following line-shape:
which is a Gaussian distribution with
Let us put in some numbers with R=8.3144598 J/K/mol at 6000cm
T = 220K, 290K
M = 16g/mol (CH
Multitply with about 1.6585 (2
Natural broadening
The Heisenberg uncertainty principle tells us that there is an uncertainty in the Energy:
As
The natural line-width is defined by using the resulting radiative lifetime, but this is mostly negligible as the natural lifetime of the upper state is usually much much smaller than the "perturbed" lifetime in the presence of quencher (e.g. through collisions or doppler broadening). Again, there are exceptions.
Collisional broadening
Collisions between molecules quench the excited state and thus reduce the lifetime of the upper state, resulting in a widening of the line width. This behavor gives rise to the so-called Lorentz lineshape.
Voigt lineshape
The Voigt line-shape is the combination of Doppler and Lorentz broadening (convolution of the two) but cannot be evaluated analytically. However, there are numerical routines to compute it efficiently, multiple of which are implemented in vSmartMOM.
Other more complex lineshapes
Once you dig deeper, there are various other more complex line-shapes (and line-mixing effects), which we ignore for now as the Voigt line-shape can provide very reasonable results. See, for instance, here.
The generated docs include a compact Plotly preview of the same line-shape families. The Voigt trace is computed as a numerical convolution of the Doppler and Lorentz profiles on the displayed grid.
Import the required tools:
using CairoMakie
using Pkg.Artifacts
using vSmartMOM
using vSmartMOM.AbsorptionRead the HITRAN database for CO
co2_par = Absorption.read_hitran(artifact("CO2"), mol=2, iso=1, ν_min=6214.4, ν_max=6214.8);
nothing #hideCreate a Voigt model (all in CPU mode)
line_voigt = make_hitran_model(co2_par, Voigt(), architecture=CPU())Create a Doppler model
line_doppler = make_hitran_model(co2_par, Doppler(), architecture=CPU())Create a Lorentz model
line_lorentz = make_hitran_model(co2_par, Lorentz(), architecture=CPU())
# Specify our wavenumber grid
ν = 6210:0.001:6220;
nothing #hideHere we compute the cross sections at different pressures (in hPa, 3rd argument) and temperatures (in K, 4th argument)
cs_co2_1atm = absorption_cross_section(line_voigt, ν, 1013.0 , 296.0);
cs_co2_075atm = absorption_cross_section(line_voigt, ν, 0.75*1013.0, 296.0);
cs_co2_05atm = absorption_cross_section(line_voigt, ν, 0.5*1013.0 , 296.0);
cs_co2_025atm = absorption_cross_section(line_voigt, ν, 0.25*1013.0, 296.0);
cs_co2_01atm = absorption_cross_section(line_voigt, ν, 0.1*1013.0 , 296.0);
nothing #hideGet some more line-shapes just for Doppler and Voigt to better compare the different line shapes
cs_co2_01atm = absorption_cross_section(line_voigt, ν, 0.1*1013.0 , 296.0);
cs_co2_doppler = absorption_cross_section(line_doppler, ν, 0.1*1013.0 , 296.0);
cs_co2_lorentz = absorption_cross_section(line_lorentz, ν, 0.1*1013.0 , 296.0);
nothing #hideUsing a scaling factor for display purposes:
ff = 1e20;
fig = Figure(size=(700, 450))
ax = Axis(fig[1,1],
xlabel = "Wavenumber (cm⁻¹)",
ylabel = "Absorption cross section (10⁻²⁰ cm²/molecule)")
lines!(ax, collect(ν), ff .* cs_co2_1atm, label="Voigt, 1 atm")
lines!(ax, collect(ν), ff .* cs_co2_075atm, label="Voigt, 0.75 atm")
lines!(ax, collect(ν), ff .* cs_co2_05atm, label="Voigt, 0.5 atm")
lines!(ax, collect(ν), ff .* cs_co2_025atm, label="Voigt, 0.25 atm")
xlims!(ax, 6214, 6215.2)
axislegend(ax, position=:rt)
figFrom these figures we can see how strongly the line-width is impacted by pressure broadening at ranges from about 250 hPa to 1013 hPa. Especially the line wings are substantially widened, which is typical of Lorentz line-shapes.
fig = Figure(size=(700, 450))
ax = Axis(fig[1,1],
xlabel = "Wavenumber (cm⁻¹)",
ylabel = "Normalized absorption cross section")
lines!(ax, collect(ν), cs_co2_01atm ./ maximum(cs_co2_01atm), label="Voigt, 296K, 0.1 atm")
lines!(ax, collect(ν), cs_co2_doppler ./ maximum(cs_co2_01atm), label="Doppler, 296K")
lines!(ax, collect(ν), cs_co2_lorentz ./ maximum(cs_co2_01atm), label="Lorentz, 296K, 0.1 atm")
xlims!(ax, 6214.4, 6214.8)
axislegend(ax, position=:rt)
figWe can see the individual impacts of Doppler and Pressure broadening at about 100 hPa, at which Doppler broadening becomes important too. Most of the shape of the wings is still dominated by pressure broadening but the center has substantial contributions from Doppler broadening as well. The Voigt shape shown here is the convolution of the Lorentz and Doppler shape.
From an individual line to a band
Here, we will compute an entire band of CO
Load a wider spectral range
co2_par_band = Absorption.read_hitran(artifact("CO2"), mol=2, iso=1, ν_min=6000.0, ν_max=6400.0);
nothing #hideCreate a Voigt model for that
band_voigt = make_hitran_model(co2_par_band , Voigt(), architecture=CPU());
nothing #hideDefine the wavenumber grid and compute cross sections at the same pressure but different temperature:
ν_band = 6300:0.01:6400;
σ_co2_Voigt220 = absorption_cross_section(band_voigt, ν_band, 1013.0 , 220.0);
σ_co2_Voigt290 = absorption_cross_section(band_voigt, ν_band, 1013.0 , 290.0);
nothing #hidefig = Figure(size=(700, 450))
ax = Axis(fig[1,1],
xlabel = "Wavenumber (cm⁻¹)",
ylabel = "σ (10⁻²⁰ cm²/molecule)")
lines!(ax, collect(ν_band), ff .* σ_co2_Voigt220, alpha=0.5, label="220K")
lines!(ax, collect(ν_band), ff .* σ_co2_Voigt290, alpha=0.5, label="290K")
xlims!(ax, 6300, 6380)
axislegend(ax, position=:rt)
figThis is still a bit hard to see as the differences are relatively subtle. However, we can already see that absorptions near the band center are stronger at lower temperatures, while the opposite is true at higher rotational states. This behavior is caused by the distribution of lower rotational states, which only occupy higher Js at higher temperatures. This picture becomes more clear if we look at differences.
fig = Figure(size=(700, 450))
ax = Axis(fig[1,1],
xlabel = "Wavenumber (cm⁻¹)",
ylabel = "Δσ (10⁻²⁰ cm²/molecule)")
lines!(ax, collect(ν_band), ff .* (σ_co2_Voigt220 .- σ_co2_Voigt290), label="220K − 290K")
xlims!(ax, 6300, 6380)
axislegend(ax, position=:rt)
figThe docs build includes an interactive Plotly summary of this temperature redistribution effect across the band:
Now we can clearly see how the change in the distribution of the lower rotational state leads to a redistribution of cross sections (a shift from lower to higher ground states with higher T)
Animated views
We can use CairoMakie's record function to create animations showing the impact of pressure and temperature:
T = 290.0
fig = Figure(size=(700, 450))
ax = Axis(fig[1,1],
xlabel = "Wavenumber (cm⁻¹)",
ylabel = "σ (10⁻²⁰ cm²/molecule)",
yscale = log10)
record(fig, joinpath(@__DIR__, "absorption_pressure.gif"), 10:10:1100; framerate=15) do p
empty!(ax)
σ = absorption_cross_section(band_voigt, ν_band, Float64(p), T)
lines!(ax, collect(ν_band), ff .* σ, label="p=$(p) hPa")
ylims!(ax, 1e-7, 1e-1)
axislegend(ax, position=:rt)
endWhen run locally, this writes absorption_pressure.gif next to the tutorial.
p = 900.0
fig = Figure(size=(700, 450))
ax = Axis(fig[1,1],
xlabel = "Wavenumber (cm⁻¹)",
ylabel = "σ (10⁻²⁰ cm²/molecule)",
yscale = log10)
record(fig, joinpath(@__DIR__, "absorption_temperature.gif"), 10:10:320; framerate=15) do T
empty!(ax)
σ = absorption_cross_section(band_voigt, ν_band, p, Float64(T))
lines!(ax, collect(ν_band), ff .* σ, label="T=$(T) K")
ylims!(ax, 1e-7, 1e-1)
axislegend(ax, position=:rt)
endWhen run locally, this writes absorption_temperature.gif next to the tutorial.
# More extreme case, let's take 10 atmospheres (10,000 hPa)
σ = absorption_cross_section(band_voigt, ν_band, 10000.0, 300.0);
fig = Figure(size=(700, 450))
ax = Axis(fig[1,1],
xlabel = "Wavenumber (cm⁻¹)",
ylabel = "σ (10⁻²⁰ cm²/molecule)")
lines!(ax, collect(ν_band), ff .* σ, label="p = 10000 hPa")
axislegend(ax, position=:rt)
figWe can see that individual lines can be blurred at very high pressures once the broadening becomes wider than the line spacing.
This page was generated using Literate.jl.