"""Andrade Safe: linear viscous SDOF, steady harmonic constant peak force.
Educational values; no machine classification, design approval or local stress.
SI inputs; X_m peak, V_rms_mm_s sinusoidal RMS, A_peak_m_s2.
Changing mass at fixed natural frequency recalculates stiffness.
"""
import math,csv

def response(m=120,fn=30,z=.04,F=160,f=30):
    values=(m,fn,z,F,f)
    if not all(math.isfinite(v) for v in values) or min(m,fn,z,F)<=0 or f<0:
        raise ValueError('Finite positive model parameters required; frequency >= 0')
    wn=2*math.pi*fn;w=2*math.pi*f;k=m*wn**2;r=f/fn
    M=1/math.hypot(1-r*r,2*z*r);X=F/k*M
    return dict(f_Hz=f,k_N_m=k,r=r,M=M,X_m=X,V_rms_mm_s=w*X/math.sqrt(2)*1000,A_peak_m_s2=w*w*X,phase_deg=math.degrees(math.atan2(2*z*r,1-r*r)),TF=math.hypot(1,2*z*r)*M)

def decrement(x0,xn,n):
    if min(x0,xn,n)<=0:raise ValueError('Positive peaks and cycle count required')
    delta=math.log(x0/xn)/n
    return delta,delta/math.sqrt(4*math.pi**2+delta**2)

if __name__=='__main__':
    rows=[response(f=f) for f in (24,30,36)]
    for row in rows:print(row)
    print('12% damping',response(z=.12))
    print('38 Hz natural frequency',response(fn=38))
    print('Free-decay decrement, damping:',decrement(4,1.8,5))
