codex/mean-aerodynamic-chord.py

mean-aerodynamic-chord.py
"""Original trapezoidal planform and chord-weighted mean; PNG in cwd."""
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon


def main():
    span, root, tip = 10., 2., 1.
    lam = tip/root
    area = span*(root+tip)/2
    mac = 2*root/3*(1+lam+lam**2)/(1+lam)
    ymac = span/6*(1+2*lam)/(1+lam)
    grid = np.linspace(-span/2,span/2,10001)
    chord = root-(root-tip)*2*np.abs(grid)/span
    np.testing.assert_allclose(np.trapezoid(chord,grid),area)
    np.testing.assert_allclose(np.trapezoid(chord**2,grid)/area,mac,atol=1e-8)
    np.testing.assert_allclose(mac,14/9)
    np.testing.assert_allclose(root-(root-tip)*2*ymac/span,mac)
    assert mac >= area/span
    fig, axes = plt.subplots(1,2,figsize=(8.6,3.2),dpi=100,facecolor='white',gridspec_kw={'width_ratios':[2.3,1]})
    ax=axes[0]
    ax.add_patch(Polygon([(-5,0),(5,0),(5,1),(0,2),(-5,1)],facecolor='#deedf7',edgecolor='#2475ad',lw=2))
    ax.plot([0,0],[0,root],color='#555555',ls='--')
    ax.plot([ymac,ymac],[0,mac],color='#b63f32',lw=3,label='Mean aerodynamic chord')
    ax.annotate(f'{mac:.4f} m',xy=(ymac,mac*.6),xytext=(3.2,1.9),arrowprops=dict(arrowstyle='->',color='#b63f32'),color='#b63f32',fontsize=11)
    ax.set(xlim=(-5.5,5.5),ylim=(2.4,-.25),xlabel='Spanwise coordinate y (m)',ylabel='Chordwise coordinate x (m)',title='Unswept symmetric trapezoidal wing')
    ax.set_aspect('equal', adjustable='box')
    ax.grid(alpha=.2)
    ax.legend(fontsize=9,loc='lower left')
    axes[1].axis('off')
    axes[1].text(.02,.9,'Constructed planform\n\nSpan: 10 m\nRoot chord: 2 m\nTip chord: 1 m\nArea: 15 m²\n\nSpan average: 1.5000 m\nChord-weighted mean:\n1.5556 m',va='top',fontsize=10,linespacing=1.25)
    fig.tight_layout()
    fig.savefig(Path.cwd()/(Path(__file__).stem+'.png'),facecolor='white')
    plt.close(fig)


if __name__=='__main__':
    main()