codex/crashworthiness-equal-work.py

crashworthiness-equal-work.py
"""Original ideal crush-force curves; Python 3.14.4, declared NumPy/Matplotlib."""
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

stroke = 0.20
x = np.linspace(0, stroke, 401)
flat = np.full_like(x, 4000.0)
ramp = 8000.0 * x / stroke
assert np.isclose(np.trapezoid(flat, x), 800.0)
assert np.isclose(np.trapezoid(ramp, x), 800.0)
fig, axes = plt.subplots(1, 2, figsize=(7.4, 3.2), sharey=True, layout="constrained", facecolor="white")
for ax, force, label, color in zip(axes, [flat, ramp], ["Constant load: peak 4 kN", "Rising load: peak 8 kN"], ["#27638f", "#a34b23"]):
    ax.fill_between(x, force / 1000, color=color, alpha=.22)
    ax.plot(x, force / 1000, color=color, lw=2.2)
    ax.text(.025, 1.4, "Work = 800 J", fontsize=11)
    ax.set(xlim=(0, .2), ylim=(0, 9), xlabel="Crush displacement (m)", title=label)
    ax.set_xticks([0, .1, .2])
    ax.grid(alpha=.2)
axes[0].set_ylabel("Resisting force (kN)")
fig.suptitle("Equal absorbed energy can require different peak loads", fontsize=12)
fig.savefig(Path("crashworthiness-equal-work.png"), dpi=140, facecolor="white")