The atmosphere and its layers

The World Meteorological Organization (WMO) defines the atmosphere as:

``A hypothetical vertical distribution of atmospheric temperature, pressure and density which by international agreement and for historical reasons, is roughly representative of year-round, midlatitude conditions.``

In fact, the atmosphere is the mean that makes the link between the ground and space and it is crucial when studying perturbations since drag affects LEO satellites.

Therefore, it was necessary to develop some mathematical model that could predict all the different conditions stated in WMO atmosphere definition for given altitudes.

Along history different models have been developed:

  • ISA: up to 11 km.

  • ISA-ICAO: up to 80 km.

  • COESA 1962: up to 700 km.

  • COESA 1976: up to 1000 km.

  • Jacchia-Roberts

Since some of them are implemented in hapsira, let us compare the differences among them:

from astropy import units as u

from matplotlib import pyplot as plt
import numpy as np

from import COESA62, COESA76

Comparing coesa62 and coesa76

Also known as U.S. Standard Atmosphere, the atmospheric model coesa76 is just an update of its little brother coesa62. The difference is that geopotential heights diverge for higher altitudes.

Let us plot the Temperature as increasing altitude for both atmospheric models:

# We build the atmospheric instances
coesa62 = COESA62()
coesa76 = COESA76()

# Create the figure
fig, ax = plt.subplots(figsize=(10, 10))
ax.set_title("U.S Standard Atmospheres")

# Collect all atmospheric models and define their plotting properties
atm_models = {
    coesa62: ["--r", "r", "Coesa 1962"],
    coesa76: ["-b", "b", "Coesa 1976"],

# Solve atmospheric temperature for each of the models
for atm in atm_models:
    z_span = np.linspace(0, 86, 100) *
    T_span = np.array([]) * u.K
    for z in z_span:
        # We discard density and pressure
        T = atm.temperature(z)
        T_span = np.append(T_span, T)

    # Temperature plot
    ax.plot(T_span, z_span, atm_models[atm][0], label=atm_models[atm][-1])
    ax.plot(atm.Tb_levels[:8], atm.zb_levels[:8], atm_models[atm][1] + "o")
    ax.set_xlim(150, 300)
    ax.set_ylim(0, 100)
    ax.set_xlabel("Temperature $[K]$")
    ax.set_ylabel("Altitude $[km]$")

# Add some information on the plot
    xy=(coesa76.Tb_levels[1].value, coesa76.zb_levels[1].value),
    xytext=(coesa76.Tb_levels[1].value + 10, coesa76.zb_levels[1].value + 5),
    arrowprops=dict(arrowstyle="simple", facecolor="black"),
    xy=(coesa76.Tb_levels[4].value, coesa76.zb_levels[4].value),
    xytext=(coesa76.Tb_levels[4].value - 25, coesa76.zb_levels[4].value + 5),
    arrowprops=dict(arrowstyle="simple", facecolor="black"),
    xy=(coesa76.Tb_levels[7].value, coesa76.zb_levels[7].value),
    xytext=(coesa76.Tb_levels[7].value + 10, coesa76.zb_levels[7].value + 5),
    arrowprops=dict(arrowstyle="simple", facecolor="black"),

# Layers in the atmosphere
for h in [11.019, 47.350, 86]:
    ax.axhline(h, color="k", linestyle="--", xmin=0.0, xmax=0.35)
    ax.axhline(h, color="k", linestyle="-", xmin=0.0, xmax=0.15)

layer_names = {
    "MESOSPHERE": 65,
for name in layer_names:
        xy=(152, layer_names[name]),
        xytext=(152, layer_names[name]),

Temperature, pressure and density distributions

One of the advantages of COESA76 is that it extends up to 1000 kilometers. The behaviour of previous magnitudes against geometrical altitude can be checked in the following figure. A logarithmic scale is applied for pressure and density to better see their decay for high altitude values:

# We create the basis for the figure
fig, axs = plt.subplots(1, 3, figsize=(12, 5))
fig.suptitle("State variables against altitude", fontweight="bold")

# Complete altitude range and initialization of state variables sets
alt_span = np.linspace(0, 1000, 1001) *
T_span = np.array([]) * u.K
p_span = np.array([]) * u.Pa
rho_span = np.array([]) * / u.m**3

# We solve for each property at given altitude
for alt in alt_span:
    T, p, rho =
    T_span = np.append(T_span, T)
    p_span = np.append(p_span,
    rho_span = np.append(rho_span, rho)

# Temperature plot
axs[0].set_xlabel("T [K]")
axs[0].set_ylabel("Altitude [K]")
axs[0].plot(T_span, alt_span)

# Pressure plot
axs[1].set_xlabel("p [Pa]")
axs[1].plot(p_span, alt_span)

# Density plot
axs[2].set_xlabel(r"$\rho$ [kg/m3]")
axs[2].plot(rho_span, alt_span)