Skip to content

Instantly share code, notes, and snippets.

@mszegedy
Created March 21, 2021 19:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mszegedy/7acbd6ff8e8cee1113d1aba6164f2175 to your computer and use it in GitHub Desktop.
Save mszegedy/7acbd6ff8e8cee1113d1aba6164f2175 to your computer and use it in GitHub Desktop.
Writing this gave me brain damage.
#!/opt/anaconda/bin/python
import os
import math
import re
import json
import csv
import copy
import itertools
import numpy as np
import scipy as sp
import scipy.signal
import matplotlib.pyplot as plt
import matplotlib.colors as clr
import seaborn as sns
sns.set()
import latex
SMOOTHING_WINDOW = 21
FWHM = 0.014
def smooth(x,window_len=11,window='hanning'):
"""smooth the data using a window with requested size.
This method is based on the convolution of a scaled window with the signal.
The signal is prepared by introducing reflected copies of the signal
(with the window size) in both ends so that transient parts are minimized
in the begining and end part of the output signal.
input:
x: the input signal
window_len: the dimension of the smoothing window; should be an odd integer
window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
flat window will produce a moving average smoothing.
output:
the smoothed signal
example:
t=linspace(-2,2,0.1)
x=sin(t)+randn(len(t))*0.1
y=smooth(x)
see also:
np.hanning, np.hamming, np.bartlett, np.blackman, np.convolve
scipy.signal.lfilter
TODO: the window parameter could be the window itself if an array instead of a string
NOTE: length(output) != length(input), to correct this: return y[(window_len/2-1):-(window_len/2)] instead of just y.
"""
if x.ndim != 1:
raise ValueError("smooth only accepts 1 dimension arrays.")
if x.size < window_len:
raise ValueError("Input vector needs to be bigger than window size.")
if window_len<3:
return x
if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
raise ValueError("Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'")
s=np.r_[x[window_len-1:0:-1],x,x[-2:-window_len-1:-1]]
#print(len(s))
if window == 'flat': #moving average
w=np.ones(window_len,'d')
else:
w=eval('np.'+window+'(window_len)')
y=np.convolve(w/w.sum(),s,mode='valid')
return y[window_len//2:-window_len//2+1]
with open('calibration-curve.csv') as c_curve_f:
global c_curve
reader = csv.reader(c_curve_f)
c_curve = np.array(list(reader), dtype=np.float64)
c_curve_smoothed = (c_curve[:11,1]+c_curve[10:,1])/2
def b(i, direction='up'):
if direction=='up':
if abs(i-round(i)) < 0.05:
return c_curve[round(i),1]
raise NotImplementedError('too far from int')
elif direction=='down':
if abs(i-round(i)) < 0.05:
return c_curve[20-round(i),1]
raise NotImplementedError('too far from int')
def b_err(i, direction='up'):
if abs(i-round(i)) > 0.05:
raise NotImplementedError('too far from int')
return abs(b(i, direction)-c_curve_smoothed[round(i)])*0.5+0.1
class MagnetSpectrum:
INTENDED_G_EFFS = {('hg', 404.7): (2,),
('hg', 435.8): (1.5, 2.),
('ne', 585.3): (1,)}
def __init__(self, path):
self.path = path
matcher = re.compile(r'([a-z]+)-([0-9.]+)-([0-9.]+)s-24deg-([0-9.]+)A.csv')
matches = matcher.match(path)
self.gas = matches.group(1)
self.intended_peak = float(matches.group(2))
self.intended_g_effs = MagnetSpectrum.INTENDED_G_EFFS[(self.gas, self.intended_peak)]
self.int_time = float(matches.group(3))
self.current = float(matches.group(4))
self.b = b(self.current)
self.b_err = b_err(self.current)
self.factor = 1 / (4.668e-8 \
* self.intended_peak**2 \
* self.b)
with open(path) as f:
reader = csv.reader(f)
self.data = np.array(list(reader)[2:], dtype=np.float64)
self.xdata = self.data.T[0]
self.inc = self.xdata[1]-self.xdata[0]
self.ydata = self.data.T[1]-np.amin(self.data.T[1])
self.ydata /= np.amax(self.ydata)
self.ydata_smoothed = smooth(self.ydata, window_len=SMOOTHING_WINDOW)
self.noise = np.linalg.norm(self.ydata-self.ydata_smoothed)/len(self.ydata_smoothed)
if self.intended_peak in (404.7, 585.3):
peak_is, _ = \
sp.signal.find_peaks(self.ydata, prominence=0.06)
self.peak_is = list(peak_is)
self.peak_ws, _, _, _ = np.array(sp.signal.peak_widths(self.ydata,
self.peak_is))*self.inc
else:
peak_is, _ = \
sp.signal.find_peaks(self.ydata_smoothed, prominence=0.01)
self.peak_is = list(peak_is)
self.peak_ws, _, _, _ = np.array(sp.signal.peak_widths(self.ydata_smoothed,
self.peak_is))*self.inc
self.peak_is.sort(key=lambda i: self.xdata[i])
self.peak_xs = np.array([self.xdata[i] for i in self.peak_is])
self.peak_ys = np.array([self.ydata[i] for i in self.peak_is])
self.peak_xerrs = 1000*self.peak_ws**2*self.noise / (2*self.peak_ys) + self.inc
if len(self.peak_is) == 1:
self.split_err = 0
self.split = 0
else:
self.split_err = np.sum(self.peak_xerrs)-self.inc*2
self.split = self.peak_xs[1]-self.peak_xs[0]
self.b_split = self.split/9.328e-8/self.intended_peak**2
self.g_eff = self.factor*self.split/2
self.e_per_m_times_b = -2*math.pi*2.998e17*self.split \
/ (np.mean(self.intended_g_effs)*self.intended_peak**2)
self.avg_peak_x = np.mean(self.peak_xs)
self.ydata_simulated = np.zeros_like(self.ydata)
for g_eff in self.intended_g_effs:
split_w = 2*g_eff/self.factor
for x in (self.avg_peak_x-split_w/2, self.avg_peak_x+split_w/2):
to_add = 1/(math.pi*FWHM/2*(1+((self.xdata-x)/FWHM*2)**2))
self.ydata_simulated += to_add
self.ydata_simulated /= np.amax(self.ydata_simulated)
class MagnetFit:
def __init__(self, spectra):
print('Magnet fit for', spectra[0].gas, 'at', spectra[0].intended_peak)
self.spectra = spectra
self.spectra.sort(key=lambda s:s.current)
self.data = np.array([[s.b, s.b_split] for s in spectra])
self.xdata = self.data.T[0]
self.ydata = self.data.T[1]
self.errs = np.array([[s.b_err, s.split_err] for s in spectra])
self.xerrs = self.errs.T[0]
self.yerrs = self.errs.T[1]*50
m, b, R, p, sigma = sp.stats.linregress(self.xdata, self.ydata)
print('m:',m,'b:',b)
print('R:',R,'sigma:',sigma)
self.m, self.b, self.R, self.p, self.sigma = m, b, R, p, sigma
self.fit_xdata = self.xdata
self.fit_ydata = self.xdata*m+b
class MagnetEPerMFit:
def __init__(self, spectra):
print('Magnet e/m fit for', spectra[0].gas, 'at', spectra[0].intended_peak)
self.spectra = spectra
self.spectra.sort(key=lambda s:s.current)
self.data = np.array([[s.b, s.e_per_m_times_b] for s in spectra])
self.xdata = self.data.T[0]
self.ydata = self.data.T[1]
self.errs = np.array([[s.b_err, s.split_err] for s in spectra])
self.xerrs = self.errs.T[0]
self.yerrs = self.errs.T[1]*50
m, b, R, p, sigma = sp.stats.linregress(self.xdata, self.ydata)
print('m:',m,'b:',b)
print('R:',R,'sigma:',sigma)
self.m, self.b, self.R, self.p, self.sigma = m, b, R, p, sigma
self.fit_xdata = self.xdata
self.fit_ydata = self.xdata*m+b
def plot_c_curve():
plt.figure()
plt.plot(c_curve.T[0], c_curve.T[1], 'ko',
c_curve.T[0], c_curve.T[1], 'k--')
plt.xlabel('Current (A)')
plt.ylabel('Magnetic field (T)')
plt.tight_layout()
plt.savefig('new_plots/c_curve.eps')
return latex.figure(
'images/c_curve.eps',
'Hysteresis loop for strength of magnets as a function of applied current.')
def plot_magnet_spectrum(s, out_path, description=''):
plt.figure()
plt.plot(s.xdata, s.ydata, 'k-',
s.xdata, s.ydata_simulated, 'k--')
plt.xlabel('Wavelength (nm)')
plt.ylabel('Intensity (normalized)')
plt.savefig('new_plots/'+out_path)
return latex.figure(
'images/'+out_path,
description)
def plot_magnet_fit_spectra(m, out_path, description=''):
plt.figure()
n = len(m.spectra)
_, ax = plt.subplots(n, 1, figsize=(4.5,n))
s_ax = None
for i, s in enumerate(m.spectra):
s_ax = ax[i]
s_ax.plot(s.xdata, s.ydata, 'k-',
s.xdata, s.ydata_simulated, 'k--')
s_ax.set_title(f'{s.current} A')
if i != n-1:
s_ax.set_xticklabels([])
s_ax.set_yticklabels([])
s_ax.set(xlabel='Wavelength (nm)')
s_ax.set(ylabel='Intensity')
plt.tight_layout()
plt.savefig('new_plots/'+out_path)
return latex.figure(
'images/'+out_path,
description)
def plot_magnet_fit(m, out_path, description=''):
plt.figure()
plt.plot(m.fit_xdata, m.fit_ydata, 'k-',
[0, 1.5], [0, 1.5*np.mean(m.spectra[-1].intended_g_effs)], 'k--')
plt.errorbar(m.xdata, m.ydata, m.yerrs, m.xerrs, 'ko')
plt.xlabel('Magnetic field (T)')
plt.ylabel('Split size (T)')
plt.savefig('new_plots/'+out_path)
return latex.figure(
'images/'+out_path,
description)
def plot_magnet_e_per_m_fit(m, out_path, description=''):
plt.figure()
plt.plot(m.fit_xdata, m.fit_ydata, 'k-',
[0, 1.5], [0, -1.5*1.7588e11], 'k--')
plt.errorbar(m.xdata, m.ydata, m.yerrs, m.xerrs, 'ko')
plt.xlabel('Magnetic field (T)')
plt.ylabel('B‧e/m (Hz)')
plt.savefig('new_plots/'+out_path)
return latex.figure(
'images/'+out_path,
description)
first_group = ['hg-404.7-4s-24deg-0A.csv', 'hg-404.7-4s-24deg-10A.csv',
'hg-404.7-4s-24deg-1A.csv', 'hg-404.7-4s-24deg-2A.csv',
'hg-404.7-4s-24deg-3A.csv', 'hg-404.7-4s-24deg-4A.csv',
'hg-404.7-4s-24deg-5A.csv', 'hg-404.7-4s-24deg-6A.csv',
'hg-404.7-4s-24deg-7A.csv', 'hg-404.7-4s-24deg-8A.csv',
'hg-404.7-4s-24deg-9A.csv']
first_group_magnet_fit = MagnetFit([MagnetSpectrum(path) for path in first_group])
first_group_magnet_e_per_m_fit = MagnetEPerMFit([MagnetSpectrum(path) for path in first_group])
second_group = ['ne-585.3-4s-24deg-0A.csv', 'ne-585.3-4s-24deg-10A.csv',
'ne-585.3-4s-24deg-1A.csv', 'ne-585.3-4s-24deg-2A.csv',
'ne-585.3-4s-24deg-3A.csv', 'ne-585.3-4s-24deg-4A.csv',
'ne-585.3-4s-24deg-5A.csv', 'ne-585.3-4s-24deg-6A.csv',
'ne-585.3-4s-24deg-7A.csv', 'ne-585.3-4s-24deg-8A.csv',
'ne-585.3-4s-24deg-9A.csv']
second_group_magnet_fit = MagnetFit([MagnetSpectrum(path) for path in second_group])
if __name__ == '__main__':
figures = [
plot_c_curve(),
plot_magnet_spectrum(first_group_magnet_fit.spectra[0], 'hg-404_7-114deg.eps'),
plot_magnet_spectrum(MagnetSpectrum('hg-435.8-2s-24deg-10A.csv'), 'hg-435_8.eps'),
plot_magnet_fit_spectra(first_group_magnet_fit, 'hg-404_7-spectra.eps'),
plot_magnet_fit(first_group_magnet_fit, 'hg-404_7-fit.eps'),
plot_magnet_fit_spectra(second_group_magnet_fit, 'ne-585_3-spectra.eps'),
plot_magnet_fit(second_group_magnet_fit, 'ne-585_3-fit.eps'),
plot_magnet_e_per_m_fit(first_group_magnet_e_per_m_fit, 'hg-404_7-e-per-m-fit.eps')]
open('new_plots/figures.tex','w').write('\n'.join(figures))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment