"""Reproduce fixture-material chart and table from cited nominal properties.
Run from site root; requires matplotlib. Values are not design allowables.
"""
from pathlib import Path
import csv, math
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
ROOT=Path.cwd()
OUT=ROOT/'dist/assets/images/vibration-notes'
DATA=ROOT/'dist/assets/data/vibration-notes'
# Exact unit definitions; 1018 source lists 29,000 ksi and 0.284 lb/in^3.
psi=0.45359237*9.80665/0.0254**2
steel_E=29000*1000*psi/1e9
steel_rho=.284*.45359237/.0254**3/1000
rows=[
 ['AZ31B magnesium',45.,1.81,'https://alliteinc.com/super-magnesium/'],
 ['6061-T6 aluminum',68.3,2.70,'https://online.kaiseraluminum.com/depot/PublicProductInformation/Document/1006/Kaiser_Aluminum_Shapes_Soft_Alloy.pdf'],
 ['1018 steel',steel_E,steel_rho,'https://www.onlinemetals.com/en/product-guide/alloy/1018']]
for r in rows:
 r.insert(3,r[1]/r[2])
assert math.isclose(rows[0][3],24.861878453038674)
assert 25.4<rows[2][3]<25.5
with (DATA/'fixture-materials.csv').open('w') as f:
 w=csv.writer(f);w.writerow(['material','youngs_modulus_GPa','density_Mg_per_m3','specific_modulus_MJ_per_kg','source_url']);w.writerows(rows)
plt.rcParams.update({'font.family':'DejaVu Sans','font.size':11,'svg.fonttype':'none','axes.spines.top':False,'axes.spines.right':False})
fig,axes=plt.subplots(1,2,figsize=(10.2,4.4),layout='constrained')
colors=['#667a63','#47768d','#616775']
for ax,col,title,xlabel,lim in [(axes[0],1,"Young’s modulus",'E (GPa)',235),(axes[1],3,'Specific stiffness','E / ρ (MJ/kg)',30)]:
 ax.barh(range(3),[r[col] for r in rows],color=colors,height=.55)
 ax.set_yticks(range(3),[r[0] for r in rows] if col==1 else [])
 ax.invert_yaxis();ax.set_xlim(0,lim);ax.set_xlabel(xlabel);ax.set_title(title,loc='left',fontsize=13,pad=13)
 ax.grid(axis='x',alpha=.18);ax.set_axisbelow(True)
 for y,r in enumerate(rows):ax.text(r[col]+lim*.025,y,f'{r[col]:.1f}',va='center',fontsize=11)
fig.suptitle('Fixture materials: stiffness and stiffness per unit density',fontsize=15,x=.02,ha='left')
fig.savefig(OUT/'fixture-materials.svg',facecolor='white')
fig.savefig(ROOT/'outputs/fixture-materials.png',dpi=180,facecolor='white')
plt.close(fig)
table=''.join(f'<tr><th scope="row">{r[0]}</th><td>{r[1]:.1f}</td><td>{r[2]:.2f}</td><td>{r[3]:.1f}</td><td><a href="{r[4]}">{name}</a></td></tr>\n' for r,name in zip(rows,['ALLITE alloy comparison','Kaiser, pp. 2–3','Online Metals product guide']))
block='''<section id="fixture-materials">
<h3>Fixture materials and specific stiffness</h3>
<p>Specific stiffness, or specific modulus, is Young’s modulus E divided by density ρ. Young’s modulus describes elastic stress divided by strain; the stiffness of a complete fixture also depends on its dimensions, joints, and loading direction.</p>
<p class="equation">Specific modulus = E / ρ</p>
<p>With E in GPa and ρ in Mg/m³, the numerical ratio is in MJ/kg, equivalent to 10⁶ m²/s². The table uses selected published nominal properties. The values support a nominal material comparison. They are not certified design allowables or measured fixture properties.</p>
<figure><img loading="lazy" src="/assets/images/vibration-notes/fixture-materials.svg" alt="Young's moduli are 45.0 GPa for AZ31B magnesium, 68.3 for 6061-T6 aluminum, and 199.9 for 1018 steel. Their specific moduli are similar: 24.9, 25.3, and 25.4 MJ per kg, respectively."><figcaption>Calculated E/ρ from the source values in the table. Both bar-chart axes start at zero; the two panels use different units. The small differences in nominal E/ρ do not establish which material is suitable for a particular fixture.</figcaption></figure>
<div class="results-scroll" role="region" aria-label="Fixture material properties" tabindex="0"><table class="evidence-table"><thead><tr><th scope="col">Material</th><th scope="col">E (GPa)</th><th scope="col">ρ (Mg/m³)</th><th scope="col">E/ρ (MJ/kg)</th><th scope="col">Property source</th></tr></thead><tbody>
'''+table+'''</tbody></table></div>
<p>The 6061-T6 entry uses the metric values printed in Kaiser’s table. The 1018 values are converted from 29,000 ksi and 0.284 lb/in³ before calculating the ratio; displayed values are rounded. Nominal values vary between references and product conditions.</p>
<p>For an ideal uniform beam, bending natural frequencies scale with √(E/ρ) when geometry and boundary conditions are fixed and the beam carries only its own distributed mass. The three materials give similar frequencies in this model. A fixture with an attached test item or flexible joints requires a model of the combined assembly.</p>
<p>At the same dimensions, steel provides a higher elastic bending stiffness EI and also contributes more moving mass. For a rectangular section, I = bh³/12, so bending stiffness varies with the cube of thickness h at fixed width b and modulus E. A lower-density material can permit a thicker section within a fixed mass budget. Check each material against the fixture geometry, payload, joint stiffness, strength requirements, and shaker limits.</p>
<p><a href="/assets/data/vibration-notes/fixture-materials.csv">Property inputs and calculated ratios (CSV)</a> · <a href="/assets/data/vibration-notes/fixture_materials.py">Calculation and figure script (Python)</a></p>
</section>
'''
for p in [ROOT/'work/vibration-notes/article.html',ROOT/'dist/blog/vibration-testing-notes/index.html']:
 s=p.read_text()
 if '<section id="fixture-materials">' in s:
  import re
  s=re.sub(r'<section id="fixture-materials">.*?</section>\n',lambda _:block,s,flags=re.S)
 else:
  marker='<h3>Baseline and low-level checks</h3>'
  assert marker in s
  s=s.replace(marker,block+marker)
 p.write_text(s)
print(rows)
