codexsurface-tension-droplet-pressure.py
surface-tension-droplet-pressure.py
"""Generate the matching PNG in CWD; tested with Python 3.14.4.
Uses the repository's NumPy and Matplotlib. Constant gamma is an assumed toy
parameter; the two-interface curve uses the equal-radius thin-film limit.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import numpy as np
def main():
gamma = 0.072 # N/m, assumed for both models
radius_mm = np.linspace(0.25, 2.0, 400)
radius_m = radius_mm * 1e-3
droplet = 2 * gamma / radius_m
bubble = 2 * droplet
assert np.all(radius_m > 0)
assert np.all(np.diff(droplet) < 0)
assert np.allclose(2 * gamma / np.array([.001, .0005]), [144, 288])
assert np.isclose(4 * gamma / .001, 288)
plt.rcParams.update({"font.size": 11, "figure.facecolor": "white",
"axes.facecolor": "white", "axes.spines.top": False,
"axes.spines.right": False})
fig, (sketch, ax) = plt.subplots(1, 2, figsize=(10.8, 5.4), dpi=110,
gridspec_kw={"width_ratios": [1, 1.25]})
fig.subplots_adjust(left=.035, right=.98, top=.82, bottom=.25, wspace=.23)
blue, orange = "#1768ac", "#bb5b12"
sketch.set_aspect("equal")
sketch.set_xlim(-1.5, 1.5)
sketch.set_ylim(-1.55, 1.5)
sketch.axis("off")
sketch.add_patch(Circle((0, 0), 1, facecolor="#e7f2fa",
edgecolor=blue, linewidth=2))
sketch.add_patch(Circle((0, 0), 1.16, fill=False, edgecolor=blue,
linestyle="--", linewidth=1.4))
sketch.annotate("", xy=(1, 0), xytext=(0, 0),
arrowprops={"arrowstyle": "->", "color": "0.2"})
sketch.text(.42, .07, "$R$", ha="center")
sketch.text(-.25, -.3, "$p_{in}$", ha="center")
sketch.text(-1.38, 1.2, "$p_{out}$")
sketch.annotate("", xy=(0, 1.38), xytext=(0, .8),
arrowprops={"arrowstyle": "->", "color": orange, "lw": 2})
sketch.text(.13, 1.19, "$dR$", color=orange)
sketch.text(0, -1.36, "One interface; virtual expansion", ha="center")
sketch.set_title("Spherical work balance", pad=14)
ax.plot(radius_mm, droplet, color=blue, lw=2.5, label=r"Droplet: $2\gamma/R$")
ax.plot(radius_mm, bubble, color=orange, lw=2.5,
label=r"Thin soap bubble: $4\gamma/R$")
for x, y, label, offset in [(.5, 288, "0.5 mm: 288 Pa", (12, 22)),
(1, 144, "1 mm: 144 Pa", (12, -17))]:
ax.scatter([x], [y], color=blue, zorder=5)
ax.annotate(label, (x, y), xytext=offset, textcoords="offset points",
fontsize=10, color=blue,
bbox={"facecolor": "white", "edgecolor": "none", "alpha": .95, "pad": 2})
ax.scatter([1], [288], color=orange, zorder=5)
ax.annotate("1 mm: 288 Pa", (1, 288), xytext=(12, 18),
textcoords="offset points", fontsize=10, color=orange,
bbox={"facecolor": "white", "edgecolor": "none", "alpha": .95, "pad": 2})
ax.set(xlabel="Radius R (mm)", ylabel="Excess pressure Δp (Pa)",
xlim=(.25, 2), ylim=(0, 1200), title="Positive radii; same γ in both models")
ax.grid(alpha=.22)
ax.legend(loc="upper right", fontsize=10, frameon=False)
fig.suptitle("Surface tension: droplet and thin-bubble pressure", fontsize=15, y=.96)
fig.text(.25, .14, r"$\Delta p\,4\pi R^2\,dR = \gamma\,8\pi R\,dR$",
ha="center", fontsize=12)
fig.text(.25, .095, "Two bubble faces double the area work.", ha="center", fontsize=10)
fig.text(.5, .035, "Assumed constant γ = 0.072 N/m; bubble thickness ≪ R. Sketch is schematic.",
ha="center", fontsize=10)
fig.savefig(Path(__file__).stem + ".png", facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()