Analyse de spectres de l’hydrogène¶
Les données que nous allons analyser sont des données de spectroscopie de l’atome de deutérieum (l’isotope de l’hydrogène de masse atomique 2). Dans le modèle simple de Bohr, les niveaux d’énergies sont donnés par :
où \(h\) et la constante de Planck, \(c\) la célérité de la lumière et \(R_D\) la constante de Rydberg du deutérium qui est reliée à la constante de Rydberg \(R_\infty\) par :
où \(m_e\) est la masse de l’électron et \(m_D\) celle du Deuteron (Noyau de l’atome de deutérium : un proton et un neutron). La constante \(R_\infty\) correspond à la constante de Rydberg pour un noyau de masse infinie.
La transition que l’on étudie est la transition entre les niveau \(n=1\) et \(n=3\). Sa fréquence vaut :
Constante de rydberg : \(R_\infty = 10973731.56816\) m\(^{-1}\)
Rapport de masse : \(\frac{m_D}{m_e} = 3670.4829679\)
Vitesse de la lumière dans le vide: \(c = 299792458\) m/s
Calculer la fréquence de la transition dans le modèle de Bohr et donner sa valeur en MHz. On formatera la valeur pour avoir trois chiffres après la virgule (précision au kHz).
import numpy as np
from matplotlib.pyplot import figure
import matplotlib.pyplot as plt
R_infty = 10973731.56816
mD_sur_me = 3670.4829679
me_sur_mD = 1/mD_sur_me
c = 299792458
RD = R_infty/(1+me_sur_mD)
freq_transition_bohr = RD*(1 - 1/9)*c
print(f'Nu Bohr : {freq_transition_bohr/1E6:.3f} MHz')
Nu Bohr : 2923507473.443 MHz
Le principe de la spectroscopie consiste a illuminer un jet d’atomes à l’aide d’un laser dont on connait la fréquence et à mesurer la fluorescence des atomes. Cette fluorescence étant très faible, on utilise un compteur de photon pour la mesurer.
Les données sont dans le fichier data/data_hydrogene_simple.txt. Il s’agit d’un fichier texte simple. Il comprend une colonne qui correspond à la fréquence (mesurée) du laser de spectroscopie et une colonne qui correspond au nombre de photons détectés sur une seconde de mesure. Ce nombre est proportionnel au nombre de photons absorbés par les atomes d’hydrogènes.
Lire et afficher les données.
freq, counts = np.loadtxt('data/data_hydrogene_simple.txt', unpack=True)
fig = figure()
ax = fig.subplots(1, 1)
ax.plot(freq, counts, '.')
ax.set_xlabel('Relative frequency [MHz]')
ax.set_ylabel('Counts [/s]')
Text(0, 0.5, 'Counts [/s]')
On va ajuster le profile par une fonction lorentzienne :
où \(a\) est un offset, \(b\) une amplitude, \(f_0\) la fréquence centrale et \(\Delta f\) la largeur en fréquence.
Faire un ajustement des données par une lorentzienne (que l’on représentera graphiquement).
from scipy.optimize import curve_fit
def fonction(x, offset, amplitude, centre, largeur):
return offset + amplitude/(1 + ((x-centre)/largeur)**2)
p0 = 100, 300, 0, 1
popt, pcov = curve_fit(fonction, freq, counts, p0)
x = np.linspace(-2, 2, 201)
fig = figure()
ax = fig.subplots(1, 1)
ax.plot(freq, counts, '.', label='Data')
ax.plot(x, fonction(x, *popt),label='Fit')
ax.set_xlabel('Relative frequency [MHz]')
ax.set_ylabel('Counts [/s]')
ax.legend()
ax.grid()
Les données sont prises par rapport à une fréquence de référence, c’est à dire que la fréquence du laser est la somme entre la fréquence de référence (\(f_\mathrm{ref} = 2923538429.3\) MHz) et la fréquence des données.
Donner la valeur de la fréquence absolue \(\nu\) ainsi que l’incertitude absolue \(\sigma_\nu\) et relative \(\sigma \nu / \nu\)) de la mesure du pic. La différence par rapport à la valeur du modèle de Bohr est dûe a des corrections supplémentaires (effet du spin de l’électron, de la taille fini du noyau, effets relativistes, ou encore de la polarization du vide qui peuvent être calculés à l’aide de l’Electrodynamique Quantique). Calculer cet écart.
freq_ref = 2923538429.3
freq_abs = freq_ref + popt[2]
sigma_abs = np.sqrt(pcov[2, 2])
print(f'Frequence : {freq_abs} MHz')
print(f'Incertitude : {sigma_abs*1e3} kHz')
print(f'Incertitude relative : {sigma_abs/freq_abs}')
print(f'Différence avec la valeur du modèle : {freq_transition_bohr*1e-6-freq_abs} MHz')
Frequence : 2923538429.2486176 MHz
Incertitude : 10.226763342276545 kHz
Incertitude relative : 3.4980772751138214e-12
Différence avec la valeur du modèle : -30955.805622577667 MHz
Les données correspondent en réalité à 6 spectres de N points qui ont été enregistrés à la suite. Ajuster chacun des 6 spectres pour en déduire leurs fréquences absolues et leurs incertitudes que l’on affichera.
Tracer avec des barres d’erreurs les points sur un graphique ayant comme axe des abscisses le numéro du spectre (1, 2, .., 6). Représenter sur ce même graphique la valeur moyenne des fréquences \(\bar{\nu}\) et son écart type \(\sigma_{\nu}\) (on utilisera une ligne continue rouge pour la moyenne et des lignes discontinues noires pour \(\bar{\nu}\pm\sigma_{\nu}\)). Est-ce que l’écart type des 6 fréquences est en accord avec l’incertitude obtenue à partir de l’ajustement de l’ensemble des données (à la question 4).
N = len(counts)//6
centers = np.zeros(6)
sigma_centers = np.zeros(6)
for i in range(6):
popt, pcov = curve_fit(fonction, freq[i*N:(i+1)*N], counts[i*N:(i+1)*N], p0)
centers[i] = popt[2]
sigma_centers[i] = np.sqrt(pcov[2,2])
#plt.plot(freq[i*N:(i+1)*N],counts[i*N:(i+1)*N],'.')
#plt.plot(x, fonction(x,*popt))
freq_abs_6 = freq_ref + centers
plt.errorbar(np.arange(6)+1, freq_abs_6, yerr=sigma_centers, fmt='o')
mean = np.mean(freq_abs_6)
std = np.std(freq_abs_6)
plt.plot([1,6],[mean, mean],'r')
plt.plot([1,6],[mean, mean]+std,'k--')
plt.plot([1,6],[mean, mean]-std,'k--')
plt.xlabel('Numéro du spectre')
plt.ylabel('Fréquence Absolue')
print(f"L'écart type des fréquence est de {std*1e3:.1f} kHz et correspond bien à celui obtenu à la question 4: {sigma_abs*1e3:.1f} kHz")
L'écart type des fréquence est de 10.0 kHz et correspond bien à celui obtenu à la question 4: 10.2 kHz
Les données du fichier data_hydrogene_simple.txt sont en fait le centre d’un spectre plus large. Le fichier data/data_complete.dat contient tous les points de mesures. Ce fichier est formaté légèrement différement (il contient un en-tête et un ‘;’ comme séparateur).
En utilsant les options de
np.loadtxtlire le fichier et tracer les points.
freq_2, counts_2 = np.loadtxt('data/data_complete.dat', skiprows=1, delimiter=';', unpack=True)
fig = figure()
ax = fig.subplots(1, 1)
ax.plot(freq_2, counts_2, '.')
ax.set_xlabel('Relative frequency [MHz]')
ax.set_ylabel('Counts [/s]')
Text(0, 0.5, 'Counts [/s]')
Ce spectre peut être modélisé par la somme d’une gaussienne (fond large) et d’une lorentzienne (étroite).
Faire l’ajustement. Donner la valeur de la position du centre de la lorentzienne étroite et son écart type. Comparer ces résultats à ceux obtenus à la question 4.
def fonction_2(x, amplitude1, centre1, largeur1, amplitude2, centre2, largeur2):
return amplitude1*np.exp(-((x-centre1)/largeur1)**2) + amplitude2/(1 + ((x-centre2)/largeur2)**2)
p0 = 100, 0, 10, 500, 0, 1
popt_2, pcov_2 = curve_fit(fonction_2, freq_2, counts_2, p0)
x = np.linspace(-30, 30, 201)
fig = figure()
ax = fig.subplots(1, 1)
ax.plot(freq_2, counts_2, '.', label='Data')
ax.plot(x, fonction_2(x, *popt_2), label='Fit')
ax.set_xlabel('Relative frequency [MHz]')
ax.set_ylabel('Counts [/s]')
Text(0, 0.5, 'Counts [/s]')
freq_abs_2 = freq_ref + popt_2[4]
sigma_abs_2 = np.sqrt(pcov_2[4, 4])
print(f'Frequence : {freq_abs_2} MHz')
print(f'Incertitude : {sigma_abs_2*1E3:.1f} kHz')
print(f'Incertitude relative : {sigma_abs_2/freq_abs_2}')
Frequence : 2923538429.2489724 MHz
Incertitude : 7.6 kHz
Incertitude relative : 2.6028203411913704e-12
A l’aide d’un masque, ajuster à l’aide d’une lorentizienne uniquement les points obtenus pour une fréquence laser comprise entre -2 et 2 MHz. Comparez avec la question 4.
mask = (freq_2 < 2) & (freq_2 > -2)
p0 = 100, 300, 0, 1
popt_tronq, pcov_tronq = curve_fit(fonction, freq_2[mask], counts_2[mask], p0)
x = np.linspace(-2, 2, 201)
fig = figure()
ax = fig.subplots(1, 1)
ax.plot(freq_2[mask], counts_2[mask],'.', label='Données tronquées')
#ax.plot(freq, counts,'.', label='Données question 4')
ax.plot(x, fonction(x, *popt),label='Fit')
ax.set_xlabel('Relative frequency [MHz]')
ax.set_ylabel('Counts [/s]')
ax.legend()
ax.grid()
freq_abs_tronq = freq_ref + popt_tronq[2]
sigma_abs_tronq = np.sqrt(pcov_tronq[2, 2])
print(f'Frequence : {freq_abs_tronq} MHz')
print(f'Incertitude : {sigma_abs_tronq*1e3} kHz')
print(f'Frequence : {freq_abs} MHz')
print(f'Incertitude : {sigma_abs*1e3} kHz')
Frequence : 2923538429.2486176 MHz
Incertitude : 10.22676753039575 kHz
Frequence : 2923538429.2486176 MHz
Incertitude : 10.226763342276545 kHz
Fonctions utilisées
from scipy.integrate import solve_ivp
from matplotlib.pyplot import errorbar
# ou ax.errorbar