Transient noise of a resistor (flicker)

ground 0 

load "res.va"
load "capacitor.osdi"

model resistor res
model isource isource
model capacitor capacitor

r1 (1 0) resistor r=1k ef=1.5 pf=1e-20 // pw=1e-22
i1 (0 1) isource dc=0

control
  abort always

  // Transient noise frequency range
  var fmin=1.0
  var fmax=1k
  
  options sweep_debug=0 op_debug=0 tran_debug=0 tran_noisedebug=1
  options rawfile="binary" strictsave=1
  options reltol=1e-3 vntol=1e-6 abstol=1e-12
  options tran_method="trap"
  options tran_laggednoise=1 
  analysis tran1 tran stop=20/fmin step=1m noisefmax=fmax noisefmin=fmin oversample=6
  analysis noise1 noise out="1" in="i1" from=fmin to=fmax mode="dec" points=10
  
  postprocess(PYTHON, "runme.py")
endc

embed "res.va" <<<FILE
`include "constants.vams"
`include "disciplines.vams"

module res(A,B);
    // Simple resistor with flicker noise source
    
    // Terminals and internal nodes
    inout A, B;
    electrical A,B;
    
    branch (A,B) br;
	
	(*desc= "Resistance", type="instance", units = "Ohm"*) parameter real r = 1 from [0:inf];
    (*desc= "Noise switch", type="instance", units = ""*)  parameter integer noisy = 1;
    (*desc= "White noise power", type="instance", units = ""*)  parameter real pw = 0;
    (*desc= "Flicker noise power", type="instance", units = ""*)  parameter real pf = 1e-20;
    (*desc= "Flicker noise exponent", type="instance", units = ""*)  parameter real ef = 1.0;
	(*desc= "Current", units ="A" *) real i;

    analog begin
        I(br) <+ V(br)/r;
        i = V(br) / r;
        if (noisy) begin
            I(br) <+ white_noise(pw, "thermal");
            I(br) <+ flicker_noise(pf, ef, "flicker");
            // $display("%f pf=%e ef=%e", $abstime, pf, ef);
        end
	end
endmodule

>>>FILE

embed "runme.py" <<<FILE
from rawfile import rawread
from vacaskpp import *
from runtest import *
import numpy as np
from scipy.signal import welch
import sys

tests=[]

plot = rawread('tran1.raw').get()
x = plot['time']
y = plot['1']/1e3
print("Samples:", x.shape[0])

noise = rawread('noise1.raw').get()
reff = noise['frequency']
refpsd = noise['onoise']/1e3**2
fmin = reff.min()
fmax = reff.max()

# Average timestep should be <= timestep limit
dx = x.max()/x.shape[0]
xs = np.arange(x[0], x[-1] + dx, dx)
ys = np.interp(xs, x, y)

# Compute nperseg so that frequency resolution is fmin/2
N = xs.shape[0]
nperseg = int(np.ceil(1/dx/(fmin/2)))
print("Interpolated points:", N)
print("Segment length:", nperseg)
print("Segments:", int(np.floor(N/(nperseg/2)-1)))

f, Pxx = welch(
    ys,
    fs=1/dx,
    window='hann',
    nperseg=nperseg,      
    noverlap=nperseg/2,      
    return_onesided=True,
    scaling='density', 
    average='mean'
)

f, Pxx = logBinMean(f, Pxx, 10)
ferr, err = errorWrtRef(reff, refpsd, f, Pxx, mode='db10')
diff = np.abs(err).max()
status = diff<3
print("max err [dB] =", diff, status)
tests.append(status)

if isTest():
    sys.exit(not all(tests))
else:
    import matplotlib.pyplot as plt 

    fig1, ax1 = plt.subplots(3, 1, figsize=(6,8), dpi=100)
    fig1.axes[0].set_title('Transient flicker noise, alpha=1.5')
    fig1.axes[0].set_ylabel('In [A]')
    fig1.axes[0].set_xlabel('time [s]')
    fig1.axes[0].plot(plot['time'], plot['1'], label="V(1)")

    fig1.axes[1].set_title('Power spectral density')
    fig1.axes[1].set_ylabel('PSD [A^2/Hz]')
    fig1.axes[1].set_xlabel('f [Hz]')

    fig1.axes[1].loglog(f, Pxx, label="PSD (log-bin mean)")
    fig1.axes[1].set_ylim(refpsd.min()/10, refpsd.max()*10)
    fig1.axes[1].loglog(reff, refpsd, 'r', label="PSD (AC noise analysis)")
    
    fig1.axes[1].legend(loc='lower left')

    Pxxi = np.interp(reff, f, Pxx)
    fig1.axes[2].set_title('Error')
    fig1.axes[2].set_ylabel('Error [dB]')
    fig1.axes[2].set_xlabel('f [Hz]')
    fig1.axes[2].semilogx(ferr, err)
    
    fig1.tight_layout()

    plt.show()
>>>FILE
