codexbernoulli-reciprocal-trajectories.py
bernoulli-reciprocal-trajectories.py
"""Exact Bernoulli trajectories; Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7.
Run from _media; writes bernoulli-reciprocal-trajectories.png to the CWD.
The curves are analytic model solutions, not measurements.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False})
fig, (ay, az) = plt.subplots(1, 2, figsize=(10.4, 4.5), dpi=120,
layout="constrained", facecolor="white")
orange, blue = "#b85116", "#196f9b"
xp = np.linspace(0, 1.999, 1200)
xn = np.linspace(0, 3, 700)
ay.plot(xp, np.exp(-xp)/(2-xp), color=orange, lw=2.6,
label=r"$a=1/2:\ y=e^{-x}/(2-x)$")
ay.plot(xn, -np.exp(-xn)/(1+xn), color=blue, lw=2.6,
label=r"$a=-1:\ y=-e^{-x}/(1+x)$")
ay.axhline(0, color="#555555", lw=1)
ay.axvline(2, color="#777777", ls=":", lw=1.5)
ay.scatter([0, 0, 1], [0.5, -1, np.exp(-1)],
color=[orange, blue, orange], zorder=4)
ay.annotate(r"minimum $(1,e^{-1})$", (1, np.exp(-1)),
xytext=(0.2, 1.25), arrowprops={"arrowstyle": "->"})
ay.annotate("blow-up at x = 2", (1.96, 3.4), xytext=(0.2, 3.2),
arrowprops={"arrowstyle": "->"}, color=orange)
ay.set(title=r"Nonlinear: $y'+y=e^x y^2$", xlabel="x", ylabel="y",
xlim=(0, 3), ylim=(-1.15, 4))
ay.legend(loc="upper right", bbox_to_anchor=(1, 0.55), fontsize=9,
framealpha=1)
xs = np.linspace(0, 2, 600)
xe = np.linspace(2, 2.25, 120)
xx = np.linspace(0, 2.25, 600)
az.plot(xs, np.exp(xs)*(2-xs), color=orange, lw=2.6,
label=r"$z=e^x(2-x)$")
az.plot(xe, np.exp(xe)*(2-xe), color=orange, lw=2.6, ls="--",
label="linear continuation only")
az.plot(xx, -np.exp(xx)*(1+xx), color=blue, lw=2.6,
label=r"$z=-e^x(1+x)$")
az.axhline(0, color="#555555", lw=1)
az.axvline(2, color="#777777", ls=":", lw=1.5)
az.scatter([0, 0, 2], [2, -1, 0], color=[orange, blue, orange], zorder=4)
az.annotate("z = 0: inverse undefined", (2, 0), xytext=(0.3, -10),
arrowprops={"arrowstyle": "->"})
az.set(title=r"Linearized exactly: $z'-z=-e^x$", xlabel="x",
ylabel="z = 1/y", xlim=(0, 2.25), ylim=(-33, 5))
az.legend(loc="lower left", fontsize=9, framealpha=1)
for ax in (ay, az):
ax.set_facecolor("white")
ax.grid(alpha=0.15)
fig.savefig(Path.cwd()/"bernoulli-reciprocal-trajectories.png",
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()