Skip to content

Commit 0d12d89

Browse files
L03: reconstruct convection-cell, lid-regimes, tidal-flexing schematics
Add matplotlib reconstruction scripts for the three remaining L03 schematic figures: - fig:convection-cells (Rayleigh-Bénard cell with hot/cold TBLs and alternating plumes) - fig:lid-regimes (mobile-lid vs stagnant-lid two-panel schematic) - fig:tidal-flexing (periapsis vs apoapsis tidal-bulge cartoon) All three regenerate the existing AVIF in place and are pure matplotlib (no external data). Captions in heat_energy.md now point at the generating script. manifest.csv updated. Closes the L03 figure-reconstruction batch.
1 parent f4e88b5 commit 0d12d89

8 files changed

Lines changed: 414 additions & 3 deletions

File tree

8.48 KB
Binary file not shown.
857 Bytes
Binary file not shown.
13.7 KB
Binary file not shown.

book/03_heat_energy/heat_energy.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ Inside solid planetary interiors, radiation is generally less important than con
165165
:width: 600px
166166
:align: center
167167
168-
Schematic of Rayleigh-Bénard convection. A fluid layer of depth $d$ is heated from below ($T_h$, hot bottom plate) and cooled from above ($T_c$, cold top plate). Hot fluid rises in plumes through a thin hot thermal boundary layer (TBL) at the base; cold fluid sinks through a thin cold TBL at the top. Lateral flow along the boundary layers closes the circulation. Convection sets in once the Rayleigh number exceeds the critical value $\mathrm{Ra}_c$ (Eq. {eq}`eq:rayleigh-number`). Custom schematic.
168+
Schematic of Rayleigh-Bénard convection. A fluid layer of depth $d$ is heated from below ($T_h$, hot bottom plate) and cooled from above ($T_c$, cold top plate). Hot fluid rises in plumes through a thin hot thermal boundary layer (TBL) at the base; cold fluid sinks through a thin cold TBL at the top. Lateral flow along the boundary layers closes the circulation. Convection sets in once the Rayleigh number exceeds the critical value $\mathrm{Ra}_c$ (Eq. {eq}`eq:rayleigh-number`). Custom schematic. Generated by `scripts/figures/L03_heat_energy/fig_convection_cells.py`.
169169
```
170170
171171
### Comparison
@@ -380,7 +380,7 @@ Most other rocky bodies in the solar system (including Venus, Mars, Mercury, and
380380
:width: 750px
381381
:align: center
382382
383-
End-member tectonic regimes for rocky planets. **Mobile lid (left):** the cold thermal boundary layer breaks into rigid plates that subduct at convergent margins, recycling lithosphere into the mantle and concentrating volcanism at ridges and hotspots; this is Earth. **Stagnant lid (right):** the boundary layer remains immobile and heat escapes by conduction through the lid, with episodic volcanic eruptions punching through; this is Mars, Venus, Mercury, and the Moon today. Custom schematic.
383+
End-member tectonic regimes for rocky planets. **Mobile lid (left):** the cold thermal boundary layer breaks into rigid plates that subduct at convergent margins, recycling lithosphere into the mantle and concentrating volcanism at ridges and hotspots; this is Earth. **Stagnant lid (right):** the boundary layer remains immobile and heat escapes by conduction through the lid, with episodic volcanic eruptions punching through; this is Mars, Venus, Mercury, and the Moon today. Custom schematic. Generated by `scripts/figures/L03_heat_energy/fig_lid_regimes.py`.
384384
```
385385
386386
Why Earth has plate tectonics while other rocky planets do not is one of the major unsolved problems in geophysics. Factors likely include:
@@ -478,7 +478,7 @@ On a circular orbit, a tidally locked moon maintains a constant distance from it
478478
:width: 700px
479479
:align: center
480480
481-
Origin of tidal heating on an eccentric orbit. **Left:** at periapsis ($r=a(1-e)$) the moon is closest to the planet and the raised tidal bulge is largest. **Right:** at apoapsis ($r=a(1+e)$) the bulge is smallest. The cyclic flexing of the body's shape on each orbit dissipates orbital energy as heat at a rate $\dot E_\mathrm{tidal}\propto e^2/Q$, where $Q$ is the tidal quality factor (orbital geometry exaggerated for clarity). Custom schematic.
481+
Origin of tidal heating on an eccentric orbit. **Left:** at periapsis ($r=a(1-e)$) the moon is closest to the planet and the raised tidal bulge is largest. **Right:** at apoapsis ($r=a(1+e)$) the bulge is smallest. The cyclic flexing of the body's shape on each orbit dissipates orbital energy as heat at a rate $\dot E_\mathrm{tidal}\propto e^2/Q$, where $Q$ is the tidal quality factor (orbital geometry exaggerated for clarity). Custom schematic. Generated by `scripts/figures/L03_heat_energy/fig_tidal_flexing.py`.
482482
```
483483
484484
The tidal heating rate depends on:
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""Generate Fig. (`fig:convection-cells`).
2+
3+
Schematic of Rayleigh-Bénard convection: a fluid layer of depth `d`
4+
heated from below at `T_h` and cooled from above at `T_c`, with thin
5+
hot and cold thermal boundary layers (TBLs) and alternating
6+
hot-rising / cold-sinking plumes that close the circulation.
7+
8+
Caption / figure id : `fig:convection-cells`
9+
Markdown source : book/03_heat_energy/heat_energy.md
10+
Citation key : (textbook schematic)
11+
12+
Pure schematic, no input data.
13+
"""
14+
from __future__ import annotations
15+
16+
from pathlib import Path
17+
18+
import matplotlib.pyplot as plt
19+
from matplotlib.patches import FancyArrowPatch, Rectangle
20+
21+
from scripts.figures._shared.style import apply_style, save_figure
22+
23+
24+
REPO_ROOT = Path(__file__).resolve().parents[3]
25+
OUT_AVIF = REPO_ROOT / "book/03_heat_energy/figures/rayleigh_benard_schematic.avif"
26+
27+
COLD = "#1f77b4"
28+
HOT = "#d62728"
29+
30+
31+
def make_plot() -> Path:
32+
apply_style()
33+
fig, ax = plt.subplots(figsize=(10, 5))
34+
35+
# Domain: x in [0, 10], y in [0, 5]; box from (0.5, 0.5) to (9.5, 4.5)
36+
x0, x1 = 0.5, 9.5
37+
y0, y1 = 0.5, 4.5
38+
tbl_h = 0.4
39+
40+
# Outer hot/cold plates
41+
ax.add_patch(Rectangle((x0, y1), x1 - x0, 0.25, color=COLD, alpha=0.85, zorder=1))
42+
ax.add_patch(Rectangle((x0, y0 - 0.25), x1 - x0, 0.25, color=HOT, alpha=0.85, zorder=1))
43+
44+
# Convection cell box
45+
ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0,
46+
fill=False, edgecolor="black", lw=1.4, zorder=2))
47+
48+
# Cold and hot TBLs (shaded inside box, top and bottom)
49+
ax.add_patch(Rectangle((x0, y1 - tbl_h), x1 - x0, tbl_h,
50+
color=COLD, alpha=0.18, zorder=1.5))
51+
ax.add_patch(Rectangle((x0, y0), x1 - x0, tbl_h,
52+
color=HOT, alpha=0.18, zorder=1.5))
53+
54+
# TBL labels
55+
ax.text(x0 + 0.25, y1 - tbl_h / 2, "Cold TBL", color=COLD,
56+
fontsize=10, va="center", ha="left")
57+
ax.text(x0 + 0.25, y0 + tbl_h / 2, "Hot TBL", color=HOT,
58+
fontsize=10, va="center", ha="left")
59+
60+
# Plate labels
61+
ax.text((x0 + x1) / 2, y1 + 0.45, r"$T_c$ (cold)", color=COLD,
62+
fontsize=13, ha="center", va="bottom")
63+
ax.text((x0 + x1) / 2, y0 - 0.45, r"$T_h$ (hot)", color=HOT,
64+
fontsize=13, ha="center", va="top")
65+
66+
# Plumes: alternating hot-rising / cold-sinking
67+
plume_x = [2.0, 3.7, 5.5, 7.3, 9.0]
68+
plume_kind = ["down", "up", "down", "up", "down"]
69+
for px, kind in zip(plume_x, plume_kind):
70+
if kind == "up":
71+
ax.add_patch(FancyArrowPatch(
72+
(px, y0 + tbl_h), (px, y1 - tbl_h),
73+
arrowstyle="->", mutation_scale=18, color=HOT, lw=2.0))
74+
else:
75+
ax.add_patch(FancyArrowPatch(
76+
(px, y1 - tbl_h), (px, y0 + tbl_h),
77+
arrowstyle="->", mutation_scale=18, color=COLD, lw=2.0))
78+
79+
# Plume labels
80+
ax.text(3.7, (y0 + y1) / 2, "Hot rising", color=HOT,
81+
fontsize=11, ha="center", va="center",
82+
bbox=dict(facecolor="white", edgecolor="none", pad=2))
83+
ax.text(5.5, (y0 + y1) / 2, "Cold sinking", color=COLD,
84+
fontsize=11, ha="center", va="center",
85+
bbox=dict(facecolor="white", edgecolor="none", pad=2))
86+
ax.text(7.3, (y0 + y1) / 2, "Hot rising", color=HOT,
87+
fontsize=11, ha="center", va="center",
88+
bbox=dict(facecolor="white", edgecolor="none", pad=2))
89+
90+
# Lateral boundary-layer flow arrows (start past the TBL labels)
91+
ax.add_patch(FancyArrowPatch(
92+
(x0 + 1.7, y1 - tbl_h / 2), (x0 + 3.2, y1 - tbl_h / 2),
93+
arrowstyle="->", mutation_scale=12, color=COLD, lw=1.2))
94+
ax.add_patch(FancyArrowPatch(
95+
(x1 - 3.2, y1 - tbl_h / 2), (x1 - 0.7, y1 - tbl_h / 2),
96+
arrowstyle="->", mutation_scale=12, color=COLD, lw=1.2))
97+
ax.add_patch(FancyArrowPatch(
98+
(x0 + 1.7, y0 + tbl_h / 2), (x0 + 3.2, y0 + tbl_h / 2),
99+
arrowstyle="->", mutation_scale=12, color=HOT, lw=1.2))
100+
ax.add_patch(FancyArrowPatch(
101+
(x1 - 3.2, y0 + tbl_h / 2), (x1 - 0.7, y0 + tbl_h / 2),
102+
arrowstyle="->", mutation_scale=12, color=HOT, lw=1.2))
103+
104+
# Heat in / heat out arrows outside box
105+
ax.add_patch(FancyArrowPatch(
106+
(x1 + 0.4, y1 + 0.1), (x1 + 0.4, y1 + 0.8),
107+
arrowstyle="->", mutation_scale=15, color=COLD, lw=1.5))
108+
ax.text(x1 + 0.6, y1 + 0.55, r"heat out $q$",
109+
color=COLD, fontsize=10, va="center")
110+
ax.add_patch(FancyArrowPatch(
111+
(x0 + 0.6, y0 - 0.7), (x0 + 0.6, y0 - 0.1),
112+
arrowstyle="->", mutation_scale=15, color=HOT, lw=1.5))
113+
ax.text(x0 + 0.85, y0 - 0.45, r"heat in $q$",
114+
color=HOT, fontsize=10, va="center")
115+
116+
# Depth indicator d on the right
117+
ax.annotate("", xy=(x1 + 0.05, y0), xytext=(x1 + 0.05, y1),
118+
arrowprops=dict(arrowstyle="<->", lw=1.0, color="black"))
119+
ax.text(x1 + 0.15, (y0 + y1) / 2, r"$d$", fontsize=14, va="center")
120+
121+
ax.set_xlim(-0.2, 10.5)
122+
ax.set_ylim(-0.6, 5.4)
123+
ax.set_aspect("equal")
124+
ax.axis("off")
125+
fig.tight_layout()
126+
return save_figure(fig, OUT_AVIF, avif_quality=80)
127+
128+
129+
def main() -> None:
130+
out = make_plot()
131+
print(f" plot : {out}")
132+
133+
134+
if __name__ == "__main__":
135+
main()
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"""Generate Fig. (`fig:lid-regimes`).
2+
3+
End-member tectonic regimes for rocky planets: mobile lid (left,
4+
plate tectonics on Earth) vs stagnant lid (right, Mars / Venus /
5+
Mercury / Moon).
6+
7+
Caption / figure id : `fig:lid-regimes`
8+
Markdown source : book/03_heat_energy/heat_energy.md
9+
Citation key : (textbook schematic; cited as Stevenson2003 / textbook)
10+
11+
Pure schematic, no input data.
12+
"""
13+
from __future__ import annotations
14+
15+
from pathlib import Path
16+
17+
import matplotlib.pyplot as plt
18+
import numpy as np
19+
from matplotlib.patches import Ellipse, FancyArrowPatch, Rectangle
20+
21+
from scripts.figures._shared.style import apply_style, save_figure
22+
23+
24+
REPO_ROOT = Path(__file__).resolve().parents[3]
25+
OUT_AVIF = REPO_ROOT / "book/03_heat_energy/figures/lid_regimes.avif"
26+
27+
MANTLE = "#f5d3a0" # warm sandy
28+
LID = "#5b8caf" # blue-grey
29+
RED = "#d62728"
30+
BLUE_DK = "#1f4e79"
31+
GREY = "#444444"
32+
33+
34+
def draw_panel(ax, mode: str) -> None:
35+
"""Draw one panel; mode in {'mobile', 'stagnant'}."""
36+
# Panel frame: x in [0, 10], mantle 0-3, lid 3-3.7, surface above 3.7
37+
x0, x1 = 0.0, 10.0
38+
y_floor = 0.0
39+
y_lid_bottom = 3.0
40+
y_lid_top = 3.7
41+
y_top = 4.2
42+
43+
# Mantle
44+
ax.add_patch(Rectangle((x0, y_floor), x1 - x0, y_lid_bottom - y_floor,
45+
color=MANTLE, zorder=1))
46+
# Lid
47+
ax.add_patch(Rectangle((x0, y_lid_bottom), x1 - x0, y_lid_top - y_lid_bottom,
48+
color=LID, alpha=0.85, zorder=1.5))
49+
50+
# Outline
51+
ax.add_patch(Rectangle((x0, y_floor), x1 - x0, y_lid_top - y_floor,
52+
fill=False, edgecolor="black", lw=1.4, zorder=3))
53+
54+
ax.text((x0 + x1) / 2, 0.4, "core", color=GREY,
55+
fontsize=10, ha="center", va="center", zorder=4)
56+
57+
if mode == "mobile":
58+
ax.set_title("Mobile lid: plate tectonics (Earth)", fontsize=12, pad=8)
59+
60+
# Ridge in middle (small gap in lid)
61+
ridge_x = 3.0
62+
ax.add_patch(Rectangle((ridge_x - 0.07, y_lid_bottom),
63+
0.14, y_lid_top - y_lid_bottom,
64+
color="white", zorder=2))
65+
66+
# Two diverging arrows above ridge
67+
ax.add_patch(FancyArrowPatch(
68+
(ridge_x, y_lid_top - 0.05), (ridge_x - 1.0, y_lid_top + 0.4),
69+
arrowstyle="->", mutation_scale=14, color=RED, lw=1.5, zorder=4))
70+
ax.add_patch(FancyArrowPatch(
71+
(ridge_x, y_lid_top - 0.05), (ridge_x + 1.0, y_lid_top + 0.4),
72+
arrowstyle="->", mutation_scale=14, color=RED, lw=1.5, zorder=4))
73+
ax.text(ridge_x - 1.2, y_top, "ridge", color=RED, fontsize=11, ha="left")
74+
75+
# Subducting slab: curved line plus tail
76+
slab_t = np.linspace(0, 1, 50)
77+
slab_x = ridge_x + 0.3 + 1.5 * slab_t
78+
slab_y = y_lid_bottom - 1.6 * slab_t ** 1.6
79+
ax.plot(slab_x, slab_y, color=BLUE_DK, lw=3.5, zorder=4)
80+
# Arrow tip on slab
81+
ax.add_patch(FancyArrowPatch(
82+
(slab_x[-3], slab_y[-3]), (slab_x[-1], slab_y[-1]),
83+
arrowstyle="->", mutation_scale=18, color=BLUE_DK, lw=3.0, zorder=4))
84+
ax.text(slab_x[-1] - 0.1, slab_y[-1] - 0.3, "subducting\nslab",
85+
color=BLUE_DK, fontsize=10, ha="center", va="top")
86+
87+
# Hotspot plume on right
88+
hs_x = 7.5
89+
ax.add_patch(FancyArrowPatch(
90+
(hs_x, 0.6), (hs_x, y_top),
91+
arrowstyle="->", mutation_scale=15, color=RED, lw=2.0, zorder=4))
92+
ax.text(hs_x, y_top + 0.1, "hotspot", color=RED, fontsize=11, ha="center")
93+
94+
else:
95+
ax.set_title("Stagnant lid (Mars, Venus, Moon)", fontsize=12, pad=8)
96+
ax.text((x0 + x1) / 2, (y_lid_top + y_lid_bottom) / 2,
97+
"rigid stagnant lid", color=BLUE_DK, fontsize=12,
98+
ha="center", va="center", weight="bold", zorder=4)
99+
100+
# Conductive heat-loss arrows across lid (small upward grey)
101+
for hx in [1.5, 4.5, 6.0, 8.5]:
102+
ax.add_patch(FancyArrowPatch(
103+
(hx, y_lid_top - 0.05), (hx, y_top),
104+
arrowstyle="->", mutation_scale=10, color=GREY, lw=1.0, zorder=4))
105+
ax.text(2.5, y_top + 0.05, "conductive heat loss",
106+
color=GREY, fontsize=11, ha="center")
107+
108+
# Volcanism: thick red arrow puncturing the lid in middle
109+
vx = 5.0
110+
ax.add_patch(FancyArrowPatch(
111+
(vx, 1.0), (vx, y_top),
112+
arrowstyle="->", mutation_scale=18, color=RED, lw=2.6, zorder=5))
113+
ax.text(vx, y_top + 0.05, "volcanism",
114+
color=RED, fontsize=11, ha="center")
115+
116+
# Sub-lid convection cells (two ellipses with circulating arrows)
117+
for cx in [2.5, 7.5]:
118+
ax.add_patch(Ellipse((cx, 1.6), 2.4, 1.0, fill=False,
119+
edgecolor=RED, lw=1.3, zorder=4))
120+
# small arrow on ellipse to indicate direction
121+
ax.add_patch(FancyArrowPatch(
122+
(cx + 0.05, 1.1), (cx + 0.55, 1.4),
123+
arrowstyle="->", mutation_scale=10, color=RED, lw=1.2, zorder=5))
124+
125+
ax.set_xlim(x0 - 0.3, x1 + 0.3)
126+
ax.set_ylim(y_floor - 0.3, y_top + 0.6)
127+
ax.set_aspect("equal")
128+
ax.axis("off")
129+
130+
131+
def make_plot() -> Path:
132+
apply_style()
133+
fig, axes = plt.subplots(1, 2, figsize=(14, 4.6))
134+
draw_panel(axes[0], "mobile")
135+
draw_panel(axes[1], "stagnant")
136+
fig.tight_layout()
137+
return save_figure(fig, OUT_AVIF, avif_quality=80)
138+
139+
140+
def main() -> None:
141+
out = make_plot()
142+
print(f" plot : {out}")
143+
144+
145+
if __name__ == "__main__":
146+
main()

0 commit comments

Comments
 (0)