codexunilateral-contact-load-release.py
unilateral-contact-load-release.py
"""Original contact-complementarity set and initial block lift-off response."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
mass, gravity = 2.0, 9.81
pull = np.linspace(0, 30, 1501)
reaction = np.maximum(mass * gravity - pull, 0)
acceleration = np.maximum((pull - mass * gravity) / mass, 0)
assert np.allclose(reaction + pull - mass * gravity, mass * acceleration)
assert np.allclose(reaction * acceleration, 0)
assert np.isclose(mass * gravity - 5, 14.62)
assert np.isclose((25 - mass * gravity) / mass, 2.69)
fig, (allowed, release) = plt.subplots(1, 2, figsize=(10, 4.3), dpi=100,
layout="constrained")
fig.set_facecolor("white")
allowed.axvspan(0, 2.2, color="#eeeeee", zorder=0)
allowed.plot([0, 0], [0, 25], color="#286f99", linewidth=3)
allowed.plot([0, 2.2], [0, 0], color="#286f99", linewidth=3)
allowed.scatter([0, 0, 1.6], [0, 15, 0], color="#286f99", s=38, zorder=4)
allowed.scatter([1.0], [10], color="#9f4b27", marker="x", s=65, linewidth=2, zorder=5)
allowed.annotate("Loaded contact", (0, 15), xytext=(0.32, 18), fontsize=9,
arrowprops={"arrowstyle": "->", "color": "#286f99"})
allowed.annotate("Unloaded touch", (0, 0), xytext=(0.25, 4), fontsize=9,
arrowprops={"arrowstyle": "->", "color": "#286f99"})
allowed.annotate("Open contact", (1.6, 0), xytext=(1.27, 3), fontsize=9,
arrowprops={"arrowstyle": "->", "color": "#286f99"})
allowed.text(0.75, 12, "Positive gap and force:\nexcluded", fontsize=9, color="#9f4b27")
allowed.set(xlim=(-0.08, 2.2), ylim=(-1, 25), xticks=[0, 1, 2],
xlabel="Normal gap (mm)", ylabel="Normal force (N)",
title="Complementarity allows only the blue axes")
force_line, = release.plot(pull, reaction, color="#286f99", linewidth=2,
label="Initial normal reaction")
release.set(xlim=(0, 30), ylim=(-1, 21), xlabel="Upward applied force (N)",
ylabel="Initial normal reaction (N)", title="Lift-off when the upward pull exceeds mg")
release.tick_params(axis="y", labelcolor="#286f99")
accel_axes = release.twinx()
accel_line, = accel_axes.plot(pull, acceleration, "--", color="#9f4b27", linewidth=2,
label="Initial upward acceleration")
accel_axes.set(ylim=(-0.28, 5.88), ylabel="Initial upward acceleration (m/s²)")
accel_axes.tick_params(axis="y", labelcolor="#9f4b27")
release.axvline(mass * gravity, color="#777777", linestyle=":", linewidth=1)
release.scatter([5], [14.62], color="#286f99", zorder=5)
accel_axes.scatter([25], [2.69], color="#9f4b27", zorder=5)
release.annotate("5 N pull: 14.62 N reaction", (5, 14.62), xytext=(2, 10.5), fontsize=8.5,
arrowprops={"arrowstyle": "->", "color": "#286f99"})
release.legend(handles=[force_line, accel_line], loc="upper right", fontsize=8)
for ax in (allowed, release):
ax.grid(alpha=0.12)
fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()