codex/voltage-divider-load-and-readout-comparison.py

voltage-divider-load-and-readout-comparison.py
"""Original divider circuits; tested with Python 3.14 and matplotlib 3.10.7.

Run from _media; output is an opaque, white PNG in the current directory.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle

plt.rcParams.update({'font.size': 12, 'font.family': 'DejaVu Sans'})
fig, axes = plt.subplots(1, 3, figsize=(12, 6.2))
fig.patch.set_facecolor('white')
blue, orange = '#185b8c', '#b95415'


def wire(ax, x1, y1, x2, y2, color=blue):
    ax.plot([x1, x2], [y1, y2], lw=2, color=color)


def resistor(ax, x, lo, hi, text, color=blue, side='left'):
    mid = (lo+hi)/2
    wire(ax, x, lo, x, mid-.3, color)
    wire(ax, x, mid+.3, x, hi, color)
    ax.add_patch(Rectangle((x-.16, mid-.3), .32, .6, facecolor='white', edgecolor=color, lw=2))
    ax.text(x-.27 if side=='left' else x+.27, mid, text,
            ha='right' if side=='left' else 'left', va='center', color=color)


cases = [('No external load', None, '5.000000 V', r'$I_L=0$'),
         ('Strong loading', 10_000, '3.333333… V', r'$I_L=1/3\ \mathrm{mA}$'),
         ('Weak loading', 1_000_000, '4.975124… V', r'$I_L=5/1005\ \mathrm{mA}$')]
for ax, (title, load, readout, current) in zip(axes, cases):
    ax.set_xlim(-.1, 4.25); ax.set_ylim(-1.1, 5.6); ax.axis('off')
    ax.set_title(title, fontweight='bold', pad=12)
    ax.text(1.7, 5.12, 'Input held at +10 V', ha='center', color=blue)
    wire(ax, 1.7, 4.85, 1.7, 4.5)
    resistor(ax, 1.7, 2.5, 4.5, '$R_1$\n10 kΩ')
    resistor(ax, 1.7, .5, 2.5, '$R_2$\n10 kΩ')
    wire(ax, 1.7, .5, 3.05, .5)
    ax.plot(1.7, 2.5, 'o', color=blue, ms=5)
    wire(ax, 1.7, 2.5, 3.05, 2.5)
    ax.text(1.72, 2.69, '$V_{out}$', color=blue, fontsize=12)
    if load:
        label = '$R_L$\n10 kΩ' if load==10_000 else '$R_L$\n1 MΩ'
        resistor(ax, 3.05, .5, 2.5, label, orange, 'right')
    else:
        ax.plot([3.05, 3.05], [.5, 2.5], 'o', mfc='white', mec=blue, ms=6)
        ax.text(3.25, 1.5, 'Open', va='center', color=blue)
    wire(ax, 1.7, .5, 1.7, .22)
    for width, y in [(.55, .22), (.36, .11), (.18, 0)]:
        wire(ax, 1.7-width/2, y, 1.7+width/2, y)
    ax.text(1.7, -.2, 'Reference: 0 V', ha='center', va='top', color=blue)
    ax.text(2.0, -.68, current, ha='center', color=orange if load else blue)
    ax.text(2.0, -1.0, readout, ha='center', va='top', fontsize=16, fontweight='bold',
            bbox={'boxstyle':'round,pad=.4', 'facecolor':'#f2f6f9', 'edgecolor':blue})
fig.suptitle('Attaching a load changes the voltage being read', fontsize=18, y=.97)
fig.text(.5, .035, 'Predictions for fixed, ideal resistors and a 10 V ideal source. Readout adds no further load.\n'
         'Each finite load is in parallel with the lower arm; these are not measured results.',
         ha='center', va='bottom', fontsize=11)
fig.subplots_adjust(left=.04, right=.95, top=.84, bottom=.22, wspace=.27)
fig.savefig(Path.cwd() / (Path(__file__).stem + '.png'), dpi=110, facecolor='white')
plt.close(fig)