# Import dei pacchetti
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.optimize import curve_fit
Si importano i dati misurati con i cursori (x,y) manualmente dall'oscilloscopio
data = pd.read_excel("scan.xlsx")
data
Vbias (V) | Bias effettivo | PP12 (mV) | PP34 (mV) | T (°C) | Unnamed: 5 | Unnamed: 6 | |
---|---|---|---|---|---|---|---|
0 | 57.00 | 56.85 | 41.00 | 43.50 | 28.2 | NaN | Gain 36 dB |
1 | 56.50 | 56.34 | 36.75 | 36.25 | 28.1 | NaN | 5mV circa spessore traccia |
2 | 56.00 | 55.83 | 31.25 | 31.00 | NaN | NaN | NaN |
3 | 55.50 | 55.32 | 28.25 | 28.00 | NaN | NaN | NaN |
4 | 55.00 | 54.81 | 25.00 | 24.00 | NaN | NaN | NaN |
5 | 54.50 | 54.35 | 19.25 | 21.00 | NaN | NaN | NaN |
6 | 54.25 | 54.10 | 17.50 | 16.00 | NaN | NaN | NaN |
7 | 54.00 | 53.85 | 15.50 | 15.00 | NaN | NaN | NaN |
8 | 53.75 | 53.53 | 12.50 | 14.75 | NaN | NaN | NaN |
9 | 53.50 | 53.36 | 15.25 | -1.00 | NaN | NaN | NaN |
10 | 53.25 | 53.09 | 8.50 | -1.00 | NaN | NaN | NaN |
11 | 53.00 | -1.00 | -1.00 | -1.00 | NaN | NaN | NaN |
Si convertono i dati da liste a vettori di numpy
V = np.array(list(map(float, list(data['Bias effettivo'])))) ;
pp12 = np.array(list(map(float, list(data['PP12 (mV)']))))
pp34 = np.array(list(map(float, list(data['PP34 (mV)']) )))
Si settano dei tagli nei dati dopo aver estratto le colonne d'interesse. I tagli hanno lo scopo di eliminare i dati non utili ai fini dell'analisi.
threshold_pp12 = np.min(np.array([V[V>0].size , pp12[pp12>0].size]))
threshold_pp34 = np.min(np.array([V[V>0].size , pp34[pp34>0].size]))
V_pp12 = V[ : threshold_pp12 ] ; V_pp34 = V[ : threshold_pp34 ] ;
pp12 = pp12[ : threshold_pp12] ; pp34 = pp34[ : threshold_pp34 ]
V_pp12 = V_pp12[: : -1] ; V_pp34 = V_pp34[: : -1]
pp12 = pp12[ : : -1] ; pp34 = pp34[: : -1]
print(pp12 , V_pp12)
[ 8.5 15.25 12.5 15.5 17.5 19.25 25. 28.25 31.25 36.75 41. ] [53.09 53.36 53.53 53.85 54.1 54.35 54.81 55.32 55.83 56.34 56.85]
Si definiscono le funzioni di fit, entrambi lineari.
def f(x,m,q):
return m*x + q
# Fit per la calibrazione --> G = dV * Cd / e con dV = Vb - Vbd, Cd capacità della singola cella in rb
def G(vb,vbd,cd):
e = 1.58e-19
cd = 5e-14 # --> Assumendo che le celle siano tutte "uguali" si considera una capacità di 50 fF
return (vb-vbd)*cd/e
Si esegue il fit lineare dei dati relativi alle distanze tra i picchi 2-1. L'errore associato alla misura del picco è la larghezza della banda osservabile, alla metà della quale è stato misurato il dato.
stef = 2
err = np.ones(pp12.size)*stef*np.sqrt(2)
popt12, optcov12 = curve_fit(f, V_pp12, pp12, p0 = None , sigma= err, absolute_sigma = True)
m12,q12 = popt12
y_new12 = f(V_pp12, m12, q12)
m12_round = round(m12,2) ; q12_round = round(q12,2)
err_m12_round = round(optcov12[0,0]**(1/2),2) ; err_q12_round = round(optcov12[1,1]**(1/2),2)
plt.figure()#figsize=(12,5))
plt.title("Stima del breakdown voltage",fontsize = 16)
plt.errorbar(V_pp12, pp12, stef, 0.001, marker='*' , markersize = 7, linestyle = '', color = 'red', label='Dati')
plt.ylabel("$\Delta pp_{21}$ [ADC]", fontsize = 14)
plt.xlabel('V bias [V]', fontsize = 14)
plt.grid('True', alpha = 0.3)
plt.plot(V_pp12, y_new12, linewidth = 1 , linestyle = '--', label='Fit')
plt.legend(loc = "lower right", fontsize = 14)
plt.show()
myChi2_12 = np.sqrt(np.sum(((pp12-f(V_pp12, *popt12))/stef )**2)) / (len(pp12) - len(popt12) )
print(myChi2_12)
0.22818639233754043
Lo stesso procedimento è applicato alle misure di distanza tra i picchi 4 e 3.
t = np.array([28.2 , 28.1])
t_m = np.mean(t) ; err_t = np.std(t)
err = np.ones(pp34.size)*stef*np.sqrt(2)
popt34, optcov34 = curve_fit(f, V_pp34, pp34, p0 = None , sigma= err, absolute_sigma = True)
m34,q34 = popt34
y_new34 = f(V_pp34, m34, q34)
m34_round = round(m34,2) ; q34_round = round(q34,2)
err_m34_round = round(optcov34[0,0]**(1/2),2) ; err_q34_round = round(optcov34[1,1]**(1/2),2)
plt.figure()#figsize=(12,5))
plt.title("Stima del breakdown voltage",fontsize = 16)
plt.errorbar(V_pp34, pp34, stef, 0.001, linestyle = '', marker='*', markersize = 7, color = 'red',label='Dati')
plt.ylabel("$\Delta pp_{43}$ [ADC]", fontsize = 14)
plt.xlabel('V bias [V]', fontsize = 14)
plt.grid('True', alpha = 0.3)
plt.plot(V_pp34, y_new34, linewidth = 1 , linestyle = '--', label='Fit')
plt.legend(loc = "lower right",fontsize = 14)
plt.show()
myChi2_34 = np.sqrt(np.sum(((pp34-f(V_pp34, *popt34))/stef )**2)) / (len(pp34) - len(popt34) )
print(myChi2_34)
m = np.array([popt12[0], popt34[0]]) ;
dm = np.array([optcov12[0,0], optcov34[0,0]]) ;
pm = 1/(dm**2)
q = np.array([popt12[1], popt34[1]]) ;
dq = np.array([optcov12[1,1] , optcov34[1,1]]) ;
pq = 1/(dq**2)
cov = np.array([optcov12[1,0], optcov34[1,0]]) ;
r = -q/m
err_r = np.sqrt( dq / m**2 + q**2 / m**4 * dm + 2 * q/ m**3 * cov )
p = 1/(err_r**2)
print(r)
mean_r = (np.sum(r*(1/err_r**2))/(np.sum(p)))
err_mean = 1/(np.sqrt(np.sum(p)))
print('La tensione di breakdown media estrapolata è:', mean_r, "con incertezza:", err_mean)
0.2853880521056999 [51.90152462 52.03777383] La tensione di breakdown media estrapolata è: 51.959270789694024 con incertezza: 6.974433535015881
Si eseguono gli stessi procedimenti di fit per i dati acquisiti dallo spettro in ADC manualmente con un cursore.
data_spectrum = pd.read_excel("scan_spectrum.xlsx")
data_spectrum
Vbias (V) | Picco1 (ADC) | Picco2 (ADC) | Picco3 (ADC) | Picco4 (ADC) | Dpp21 | Dpp43 | |
---|---|---|---|---|---|---|---|
0 | 57.00 | 950.0 | 1885.0 | 2806.0 | 3740.0 | 935 | 934 |
1 | 56.50 | 852.0 | 1698.0 | 2535.0 | 3373.0 | 846 | 838 |
2 | 56.00 | 754.0 | 1507.0 | 2255.0 | 3003.0 | 753 | 748 |
3 | 55.50 | 658.0 | 1319.0 | 1976.0 | 2621.0 | 661 | 645 |
4 | 55.00 | 558.0 | 1128.0 | 1691.0 | 2242.0 | 570 | 551 |
5 | 54.50 | 471.0 | 930.0 | 1400.0 | 1864.0 | 459 | 464 |
6 | 54.25 | 416.0 | 837.0 | 1245.0 | 1673.0 | 421 | 428 |
7 | 54.00 | 376.0 | 738.0 | 1109.0 | 1480.0 | 362 | 371 |
8 | 53.75 | 321.0 | 651.0 | 954.0 | 1276.0 | 330 | 322 |
9 | 53.50 | 267.0 | 540.0 | 812.0 | 1078.0 | 273 | 266 |
10 | 53.25 | 222.0 | 432.0 | 662.0 | 878.0 | 210 | 216 |
11 | 53.00 | 173.0 | 333.0 | 513.0 | -1.0 | 160 | -514 |
V_s = np.array(list(map(float, list(data_spectrum['Vbias (V)'])))) ;
d21 = np.array(list(map(float, list(data_spectrum['Dpp21']))))
d43 = np.array(list(map(float, list(data_spectrum['Dpp43']) )))
threshold_d21 = np.min(np.array([V_s[V_s>0].size , d21[d21>0].size]))
threshold_d43 = np.min(np.array([V_s[V_s>0].size , d43[d43>0].size]))
V_d21 = V_s[ : threshold_d21 ] ; V_d43 = V_s[ : threshold_d43 ] ;
d21 = d21[ : threshold_d21] ; d43 = d43[ : threshold_d43 ]
V_d21 = V_d21[: : -1] ; V_d43 = V_d43[: : -1]
d21 = d21[: : -1] ; d43 = d43[: : -1]
err = np.sqrt(2)*np.ones(d21.size)
L'errore sulla singola misura è 1 ADC.
popt21, optcov21 = curve_fit(f, V_d21, d21 , p0 = None , sigma= err, absolute_sigma = True)
m21,q21 = popt21
y_new21 = f(V_d21, m21, q21)
m21_round = round(m21,2) ; q21_round = round(q21,2)
err_m21_round = round(optcov21[0,0]**(1/2),2) ; err_q21_round = round(optcov21[1,1]**(1/2),2)
myChi2_21 = np.sqrt(np.sum(((d21-f(V_d21, *popt21))/np.sqrt(2) )**2)) / (len(d21) - len(popt21) )
print(myChi2_21)
plt.figure()#figsize=(12,5))
plt.title("Stima del breakdown voltage",fontsize = 16)
plt.plot(V_d21, y_new21, linewidth = 1 , linestyle = '--', label='Fit')
plt.errorbar(V_d21, d21, err, 0.001 , marker='*' , markersize = 7, linestyle = '', color = 'red', label='Dati')
plt.ylabel("$\Delta pp_{21}$ [ADC]", fontsize = 14)
plt.xlabel('V bias [V]', fontsize = 14)
plt.grid('True', alpha = 0.3)
plt.legend(fontsize = 14, loc="lower right")
plt.show()
print(m21_round,q21_round)
1.984470660326928
193.24 -10069.25
err = np.sqrt(2)*np.ones(d43.size)
popt43, optcov43 = curve_fit(f, V_d43, d43 , p0 = None , sigma= err, absolute_sigma = True)
m43,q43 = popt43
y_new43 = f(V_d43, m43, q43)
m43_round = round(m43,2) ; q43_round = round(q43,2) ; print(m43_round,q43_round)
err_m43_round = round(optcov43[0,0]**(1/2),2) ; err_q43_round = round(optcov43[1,1]**(1/2),2) ; print(err_m43_round,err_q43_round)
myChi2_43 = np.sqrt(np.sum(((d43-f(V_d43, *popt43))/np.sqrt(2) )**2)) / (len(d43) - len(popt43) )
print(myChi2_43)
plt.figure()#figsize=(12,5))
plt.title("Stima del breakdown voltage",fontsize = 16)
plt.plot(V_d43, y_new43, linewidth = 1 , linestyle = '--', label='Fit')
plt.errorbar(V_d43, d43, err, 0.001 , marker='.' , markersize = 7, linestyle = '', color = 'red', label='Dati')
plt.ylabel("$\Delta pp_{43}$ [ADC]", fontsize = 14)
plt.xlabel('V bias [V]', fontsize = 14)
plt.grid('True', alpha = 0.3)
plt.legend(fontsize = 14, loc="lower right")
plt.show()
189.33 -9857.15 0.35 19.45 1.588004110408253
La misura del potenziale di breakdown si può trovare dal rapporto -q/m dei fit prima effettuati. Questa procedura ci permette di verificare se le misure effettuate sono tra loro compatibili, non solo rispetto ai picchi considerati, ma anche rispetto alle procedure stesse.
Come prima cosa si vettorializzano i valori dei coefficienti angolari e delle intercette dei fit, con le rispettive incertezze estratte dalla matrice di covarianza.
m = np.array([m21, m43]) ;
dm = np.array([optcov21[0,0], optcov43[0,0]]) ;
pm = 1/(dm**2)
q = np.array([q21 , q43]) ;
dq = np.array([optcov21[1,1] , optcov43[1,1]]) ;
pq = 1/(dq**2)
cov = np.array([optcov21[1,0], optcov43[1,0]]) ;
r = -q/m
err_r = np.sqrt( dq / m**2 + q**2 / m**4 * dm + 2 * q/ m**3 * cov )
p = 1/(err_r**2)
print(r)
[52.10861313 52.06408976]
Si definisce r il rapporto tra le intercette e i coefficienti angolari di ogni singolo fit, cambiato di segno, con il relativo errore.
Si trovano il valore medio di r come la media pesata dei risultati sopra trovati e l'incertezza associata a tale media.
mean_r = (np.sum(r*(1/err_r**2))/(np.sum(p)))
err_mean = 1/(np.sqrt(np.sum(p)))
print('La tensione di breakdown media estrapolata è:', mean_r, "con incertezza:", err_mean)
La tensione di breakdown media estrapolata è: 52.088804320122655 con incertezza: 0.1335488318930171