codex/gear-face-line-load.py

gear-face-line-load.py
"""Compare equal-total gear face loads with different peak intensities.

Tested with Python 3.14.4 and the Matplotlib/NumPy dependencies in pyproject.toml.
Writes gear-face-line-load.png to the current output directory.
"""
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np

width_mm = 20.0
force_n = 1000.0
z = np.linspace(0.0, width_mm, 401)
mean_load = force_n / width_mm
fig, axes = plt.subplots(1, 2, figsize=(7.2, 3.6), dpi=100, sharey=True)
fig.patch.set_facecolor('white')
for ax, tilt, title, color in zip(
    axes, [0.0, 0.6], ['Uniform face load', 'Tilted face load'], ['#267d91', '#b45c25']
):
    load = mean_load * (1.0 + tilt * (2.0 * z / width_mm - 1.0))
    ax.fill_between(z, load, color=color, alpha=0.16)
    ax.plot(z, load, color=color, lw=2.4)
    ax.axhline(mean_load, color='#65707b', ls='--', lw=1)
    ax.set_title(title, fontsize=11, pad=10)
    ax.text(0.04, 0.94, f'Peak / mean = {1+tilt:.1f}', transform=ax.transAxes,
            fontsize=10, va='top', color=color)
    ax.set_xlim(0, width_mm)
    ax.set_ylim(0, 105)
    ax.set_xticks([0, 10, 20])
    ax.set_yticks([0, 20, 50, 80, 100])
    ax.set_xlabel('Axial position z (mm)', fontsize=10)
    ax.grid(axis='y', alpha=0.15)
    ax.spines[['top', 'right']].set_visible(False)
axes[0].set_ylabel('Normal line load (N/mm)', fontsize=10)
fig.suptitle('Same 1000 N total load over 20 mm; different peaks', fontsize=12, y=0.97)
fig.text(0.5, 0.025, 'Both ideal profiles touch across the full width. A contact mark alone does not give load intensity.',
         ha='center', fontsize=8.2)
fig.subplots_adjust(left=0.10, right=0.98, bottom=0.21, top=0.79, wspace=0.18)
fig.savefig(Path.cwd() / 'gear-face-line-load.png', facecolor='white')
plt.close(fig)