260 lines
13 KiB
Python
260 lines
13 KiB
Python
import numpy as np
|
|
import datetime
|
|
from scipy.optimize import curve_fit
|
|
|
|
# start = [11.75-0.25*i for i in range(8)]
|
|
# print(start)
|
|
|
|
# temps = []
|
|
# for s in start:
|
|
# temps.append([s-i*0.0194 for i in range(150)])
|
|
|
|
# data = np.array(temps)
|
|
# times = [i for i in range(150)]
|
|
|
|
|
|
# Define linear fit function:
|
|
def linear_fit(x, a, b):
|
|
'''
|
|
Linear function for fitting of CRAFT data
|
|
'''
|
|
return a+b*x
|
|
|
|
# Define smooth_transition function for correcting not-linear section in the beginning of the cooldown
|
|
def smooth_transition(x, a, b, c, x0):
|
|
"""
|
|
A function that is constant before t0 and transitions smoothly into a linear function after t0.
|
|
Is needed for fitting of CRAFT data. In case the temperature does not increase linear at the
|
|
beginning of the cooldown.
|
|
Parameters:
|
|
t : array-like, time values
|
|
t0 : float, transition point
|
|
a : float, constant value before t0
|
|
b : float, slope of the linear function after t0
|
|
c : float, controls the smoothness of the transition
|
|
|
|
Returns:
|
|
array-like, function values
|
|
"""
|
|
return a + (b * (x - x0)) / (1 + np.exp(-c * np.clip(x - x0, -100, 100)))
|
|
def mean_sigma(input): #calculate average an standart deviation
|
|
'''
|
|
Calculates the mean and standard deviation of a given list of numbers.
|
|
Parameters
|
|
----------
|
|
input : list of numbers
|
|
List of numbers to be analyzed.
|
|
Returns
|
|
-------
|
|
mean : float
|
|
Mean of the input list.
|
|
sigma : float
|
|
Standard deviation of the input list.
|
|
'''
|
|
n = len(input)
|
|
mean = np.nanmean([input])
|
|
deviation = [(xi - mean)**2 for xi in input]
|
|
sigma = np.sqrt(1/(n-1)*np.nansum(deviation))
|
|
return(mean, sigma)
|
|
|
|
def find_index_of_first_equal_to(lst, value):
|
|
'''Finds the index of the first element in a list that is equal to a given value. Returns -1 if no such element is found.'''
|
|
return next((i for i, x in enumerate(lst) if x == value), np.nan)
|
|
|
|
def calc_gradient(input, times, sensors:list = [1,3,4,5,6,7,8], Tc = 9.2, return_indices: bool = False):
|
|
'''Analyses cooldown. "sensors" specifies all sensors that are being used (1 to 8)
|
|
input is np.array of temperatures
|
|
times is np.array of times
|
|
sensors is list with sensors used for analysis
|
|
Tc is critical temperature
|
|
return_indices specifies if the indices of the sensors closest to the critical temperature should be returned'''
|
|
#find indices of numbers closest to Tc
|
|
positions = [9.5 - ((x-1)*9.5/7) for x in sensors] #get the sensor position on the sample
|
|
indices = []#list of indices
|
|
#convert input to np.array if necessary
|
|
if type(input) == list:
|
|
input = np.array(input)
|
|
#convert datetime to timestamp, if necessary
|
|
if type(times[0]) == datetime.datetime:
|
|
times = [t.timestamp() for t in times]
|
|
#transpose array if fist dimension if larger than second
|
|
if input.shape[0] > input.shape[1]:
|
|
input = input.T
|
|
|
|
#find indices at which critical temperature is reached
|
|
input = input[[x-1 for x in sensors]]
|
|
for i in input:
|
|
index = (np.abs(i - Tc)).argmin()
|
|
indices.append(index)
|
|
|
|
|
|
#calculate local gradients and cooldown rates
|
|
grad_loc = [np.NaN for x in range(8)] #local temperature gradients
|
|
rate_loc = [np.NaN for x in range(8)] #local cooldown rates
|
|
|
|
for i in range(len(sensors)):
|
|
if i == 0: #for first sensor take temperature difference to the sensor below
|
|
DT = input[0,indices[0]] - input[1,indices[0]] #temperature difference
|
|
t = times[indices[0]] - times[indices[1]] #time it took the pahsefront to move from second sensor to first sensor
|
|
Ds = positions[i] - positions[i+1] #distance between sensors
|
|
grad_loc[sensors[i]-1] = (DT/Ds)
|
|
try:
|
|
rate_loc[sensors[i]-1] = (DT/t)
|
|
except ValueError:
|
|
rate_loc[sensors[i]-1] = 0
|
|
elif i == len(sensors)-1: #for last sensor take temperature difference to the sensor above
|
|
DT = input[i-1,indices[i]] - input[i,indices[i]] #temperature difference
|
|
t = times[indices[i-1]] - times[indices[i]] #time it took the pahsefront to move from sensor 7 to sensor 8
|
|
Ds = positions[i-1] - positions[i] #distance between sensors
|
|
grad_loc[sensors[i]-1] = (DT/Ds)
|
|
try:
|
|
rate_loc[sensors[i]-1] = (DT/t)
|
|
except ValueError:
|
|
rate_loc[sensors[i]-1] = 0
|
|
else: #for middle sensors take average temperature difference to sensor below and above
|
|
Ds_top = positions[i-1] - positions[i] #distance to sensor above
|
|
Ds_bot = positions[i] - positions[i+1] #distance to sensor above
|
|
DT = np.mean([input[i-1,indices[i]] - input[i,indices[i]] , input[i,indices[i]] - input[i+1,indices[i]]]) #mean temperature difference to sensor above and below
|
|
grad = np.mean([(input[i-1,indices[i]] - input[i,indices[i]]) / Ds_top , (input[i,indices[i]] - input[i+1,indices[i]]) / Ds_bot]) #mean temperature gradient to sensor above and below
|
|
t = np.mean([times[indices[i-1]] - times[indices[i]], times[indices[i]] - times[indices[i+1]]]) #mean time to sensor above and from sensor below
|
|
grad_loc[sensors[i]-1] = grad
|
|
try:
|
|
rate_loc[sensors[i]-1] = (DT/t)
|
|
except ValueError:
|
|
rate_loc[sensors[i]-1] = 0
|
|
|
|
#calc global gradient and transition time
|
|
grad_glob = (input[0,indices[-1]] - input[-1,indices[-1]]) / (positions[0] - positions[-1]) #gives gradient in K/cm. Its 9.5 cm instead of 10 cm since the cernox sensors are not at the very end of the sample. This is momentary global gradient when the lowest sensor passes Tc
|
|
trans_time = times[indices[0]] - times[indices[-1]]
|
|
(av_grad_glob, e_av_grad_glob) = mean_sigma(grad_loc) #average gradient from local gradients. The error is also returned.
|
|
(av_rate_glob, e_av_rate_glob) = mean_sigma(rate_loc) #average cooldown rate from local rates.
|
|
|
|
if return_indices:
|
|
return([grad_glob, av_grad_glob, e_av_grad_glob, trans_time, av_rate_glob, e_av_rate_glob, grad_loc, rate_loc, indices])
|
|
else:
|
|
return([grad_glob, av_grad_glob, e_av_grad_glob, trans_time, av_rate_glob, e_av_rate_glob, grad_loc, rate_loc])
|
|
|
|
def calc_gradient_CRAFT(input, times, sensors:list = [1,3,4,5,6,7,8], Tc_top = 9.2, Tc_bottom = 9.2, return_indices: bool = False):
|
|
'''Analyses cooldown. "sensors" specifies all sensors that are being used (1 to 8)
|
|
input is np.array of temperatures
|
|
times is np.array of times
|
|
sensors is list with sensors used for analysis
|
|
Tc is critical temperature
|
|
return_indices specifies if the indices of the sensors closest to the critical temperature should be returned'''
|
|
#find indices of numbers closest to Tc
|
|
sensors = [1,8] #override sensor list that is passed to function for now because only 1 and 8 are used
|
|
positions = [9.5 - ((x-1)*9.5/7) for x in sensors] #get the sensor position on the sample
|
|
indices = []#list of indices
|
|
#convert input to np.array if necessary
|
|
if type(input) == list:
|
|
input = np.array(input)
|
|
#convert datetime to timestamp, if necessary and convert it to np.array
|
|
if type(times[0]) == datetime.datetime:
|
|
times = [t.timestamp() for t in times]
|
|
times = np.array(times)
|
|
#transpose array if fist dimension if larger than second
|
|
if input.shape[0] > input.shape[1]:
|
|
input = input.T
|
|
|
|
#find indices at which critical temperature is reached
|
|
input = input[[x-1 for x in sensors]]
|
|
#Calculate t_Tc_start: Lowest timepoint where Tc is reached by Cernox 1 OR 8. "OR" is important, because CRAFT allows for negative gradients during cooldown
|
|
t_Tc_start = times[np.min(np.where((input[0] <= Tc_top) | (input[1] <= Tc_bottom)))]
|
|
#Calculate t_Tc_stop: Highest timepoint where Tc is reached by Cernox 1 OR 8
|
|
t_Tc_stop = times[np.max(np.where((input[0] >= Tc_top) | (input[1] >= Tc_bottom)))]
|
|
|
|
# Smooth_transition fit to the temperature data of Cernox 1 and 8, from t_Tc_start - t_Tc_start_delta until t_Tc_stop + t_Tc_stop_delta
|
|
# Often a linear fit is sufficient. But when the sc transitions starts immediately after cooldown start, the temperature data is not linear in the beginning.
|
|
|
|
# Timepoints t_Tc_start / _stop are calculated automatically by checking time when Cernox 1 and 8 transition Tc_top and Tc_bottom. Use this delta to adjust the time interval.
|
|
t_Tc_start_delta = 0
|
|
t_Tc_stop_delta = 0
|
|
|
|
fit_indices = np.where((times >= t_Tc_start - t_Tc_start_delta) & (times <= t_Tc_stop + t_Tc_stop_delta)) # Indices of the data array which are in the interesting time interval
|
|
t0 = times[fit_indices[0][0]] #Set t0 to the first timepoint of the fit_indices. Reduces the number of digits in the fit parameters...
|
|
t_end = times[fit_indices[0][-1]]
|
|
t_T1 = times[fit_indices] - t0
|
|
y_T1 = input[0][fit_indices]
|
|
t_T8 = times[fit_indices] - t0
|
|
y_T8 = input[1][fit_indices]
|
|
trans_time = abs(t_T1[-1])
|
|
# Fit the smooth_transition function to the data
|
|
popt_T1, pcov_T1 = curve_fit(smooth_transition, t_T1, y_T1, p0=[y_T1[0], -0.03, 0.1, 0], maxfev=10000)
|
|
popt_T8, pcov_T8 = curve_fit(smooth_transition, t_T8, y_T8, p0=[y_T8[0], -0.03, 0.1, 0], maxfev=10000)
|
|
# popt_T1, pcov_T1_linear = curve_fit(linear_fit, t_T1, y_T1)
|
|
# popt_T8, pcov_T8_linear = curve_fit(linear_fit, t_T8, y_T8)
|
|
y_T1_fit = smooth_transition(t_T1, *popt_T1)
|
|
y_T8_fit = smooth_transition(t_T8, *popt_T8)
|
|
# y_T1_fit = linear_fit(t_T1, *popt_T1)
|
|
# y_T8_fit = linear_fit(t_T8, *popt_T8)
|
|
|
|
# Linear fit for calculating cooldown speed
|
|
popt_T1_linear, pcov_T1_linear = curve_fit(linear_fit, t_T1, y_T1)
|
|
popt_T8_linear, pcov_T8_linear = curve_fit(linear_fit, t_T8, y_T8)
|
|
y_T1_linearfit = linear_fit(t_T1, *popt_T1_linear)
|
|
y_T8_linearfit = linear_fit(t_T8, *popt_T8_linear)
|
|
|
|
|
|
#Calculate cooldown speed using the linear fit.
|
|
cooldown_speed_T1 = popt_T1_linear[1]
|
|
cooldown_speed_T1_std = np.sqrt(np.diag(pcov_T1_linear))[1]
|
|
cooldown_speed_T8 = popt_T8_linear[1]
|
|
cooldown_speed_T8_std = np.sqrt(np.diag(pcov_T8_linear))[1]
|
|
#Calculate the mean cooldown speed
|
|
cooldown_speed = np.mean([cooldown_speed_T1, cooldown_speed_T8])
|
|
#Calculate the std of the mean cooldown speed
|
|
cooldown_speed_std = np.sqrt(cooldown_speed_T1_std**2 + cooldown_speed_T8_std**2)
|
|
|
|
#Calculate the mean and std temperature gradient by calc. difference of y_T1 and y_T8 for each x inbetween t_Tc_start and t_Tc_stop. Here, smooth_transition fit could be crucial!
|
|
Distance = 9.5
|
|
Gradients = (y_T1_fit - y_T8_fit) / Distance
|
|
Gradients = Gradients[np.where((t_T1 + t0 >= t_Tc_start) & (t_T1 +t0 <= t_Tc_stop))]
|
|
mean_gradient = np.mean(Gradients)
|
|
std_gradient = np.std(Gradients)
|
|
|
|
if return_indices:
|
|
indices = [fit_indices[0][0], fit_indices[0][-1]]
|
|
return([mean_gradient, mean_gradient, std_gradient, trans_time, cooldown_speed, cooldown_speed_std,
|
|
0, 0, indices, np.append(popt_T1,np.array([t0, t_end])),
|
|
np.append(popt_T8,np.array([t0, t_end]))]) #pass times with fit parameters for plotting
|
|
else:
|
|
return([mean_gradient, mean_gradient, std_gradient, trans_time, cooldown_speed, cooldown_speed_std,
|
|
0, 0, np.append(popt_T1,np.array([t0, t_end])),
|
|
np.append(popt_T8,np.array([t0, t_end]))]) #pass times with fit parameters for plotting
|
|
|
|
def analyse_B_field(input, times, timestamps):
|
|
'''
|
|
input: np.array of full data set (temperaute,AMRs,fluxgates)
|
|
times: list of times corresponding to the data points
|
|
timestamps: list of timestamps: start_ramp, stop_ramp, save_point
|
|
'''
|
|
#timestamp "stop_ramp" must be adjusted by three seconds because main records the time when the PID controllers are turned off. After that it waits 3 seconds.
|
|
# timestamps[1] = timestamps[1] +datetime.timedelta(0,3)
|
|
|
|
#find indices corresponding to timestamps
|
|
indices = [find_index_of_first_equal_to(times,timestamp) for timestamp in timestamps]
|
|
print(indices)
|
|
#calculate AMR magnitude
|
|
AMR_mag = np.zeros(shape = (len(input[:,0]),15))
|
|
for j in range(15):
|
|
for i in range(len(input[:,0])):
|
|
AMR_mag[i,j] = np.sqrt(input[i,j+8]**2 + input[i,j+23]**2 + input[i,j+38]**2)
|
|
|
|
#find B_start from fluxgates
|
|
B_start = [input[indices[0],53],input[indices[0],54],input[indices[0],55]]
|
|
B_start.append(np.sqrt(B_start[0]**2+B_start[1]**2+B_start[2]**2)) #magnitude of B_start
|
|
|
|
#find expelled flux
|
|
B_expelled = np.hstack((input[indices[1],8:53], AMR_mag[indices[1],:]))
|
|
|
|
#find trapped flux
|
|
B_trapped = np.hstack((input[indices[2],8:53], AMR_mag[indices[2],:]))
|
|
|
|
#calculate wave magnitude
|
|
WM_x = np.max(np.max(abs(input[indices[0]:indices[1],8:23]) - abs(input[indices[0],8:23])))
|
|
WM_y = np.max(np.max(abs(input[indices[0]:indices[1],23:38]) - abs(input[indices[0],23:38])))
|
|
WM_z = np.max(np.max(abs(input[indices[0]:indices[1],38:53]) - abs(input[indices[0],38:53])))
|
|
|
|
return([B_start, B_expelled, B_trapped, [WM_x, WM_y, WM_z], indices])
|
|
|
|
|