Skip to content
Snippets Groups Projects
Commit 4fe9e49d authored by Michael Pawelzik's avatar Michael Pawelzik
Browse files

bugfix in postplot module if statement

analysis functions module for correlation and regression
parent 678b92c2
No related branches found
No related tags found
1 merge request!3feat: introduce external sensors
......@@ -196,7 +196,7 @@ if __name__ == '__main__':
# plot regression coefficients
plot_obj.edit_annotation_in_plot(annotate_string = reg_coeff)
plot_obj.edit_annotation_in_plot(annotate_string = reg_coeff)
plot_obj.edit_annotation_in_plot(annotate_string = reg_coeff)
if plot_trace_curvefit == True:
......
# -*- coding: utf-8 -*-
"""
Created on Thu Jul 27 08:53:36 2023
@author: pawelzik
"""
import numpy as np
from scipy.optimize import curve_fit
class reg_and_corr:
def __init__(self, post_plot_obj, legend_loc = 'upper left', \
legend_bbox_to_anchor = (1.09, 1)):
self.post_plot_obj = post_plot_obj
self.K_phases = None
self.phase_t0 = None
self.legend_loc = legend_loc
self.legend_bbox_to_anchor = legend_bbox_to_anchor
def calc_correlation(self, func_1 , func_2 , state = 'False'):
# calulate correlation between two parameter from data frame
corr_coeff= np.corrcoef(func_1, func_2)[0][1]
annotate_string = "$R_{\phi(S_{21}),Temp_{DUT}}$ = %.3f" % corr_coeff
return annotate_string
def calc_regression_coeff_phase_S21(self, data_frame, time_unit = 'min', state = False):
phases = data_frame.S21_PHASE.tolist()
self.phase_t0 = phases[0]
self.K_phases = np.median(data_frame.S21_PHASE)
# time stamp of index = 0 is the start point of time axis, Michael
# substract all other timestamps with time stamp of index zero to get time scale in
# seconds, Michael
time_sec = data_frame.TIMESTAMP - data_frame.TIMESTAMP[0]
# set scaling of time axis depending on the time unit given in parameter list, Michael
# default unit is minutes, Michael
time_vals = self.scaling_time_axes(time_sec, time_unit)
func, popt = self.choose_fit(time_vals, data_frame.S21_PHASE)
annotate_string_fit = self.plot_fitted_func_param(func, *popt, time_unit = time_unit)
return annotate_string_fit
def plot_phase_curve_fit(self, data_frame, postplot_obj, time_unit = 'min', state = False):
if state == True:
phases = data_frame.S21_PHASE.tolist()
self.phase_t0 = phases[0]
self.K_phases = np.median(data_frame.S21_PHASE)
# time stamp of index = 0 is the start point of time axis, Michael
# substract all other timestamps with time stamp of index zero to get time scale in
# seconds, Michael
time_sec = data_frame.TIMESTAMP - data_frame.TIMESTAMP[0]
# set scaling of time axis depending on the time unit given in parameter list, Michael
# default unit is minutes, Michael
time_vals = self.scaling_time_axes(time_sec, time_unit)
# call chose fit function
func, popt = self.choose_fit(time_vals, data_frame.S21_PHASE)
# genrate trace for curvefit
fit_phaeses = func(time_vals, *popt)
# determine max, min of measured phases
min_phases, max_phases = self.get_extended_min_max(data_frame.S21_PHASE)
# determine max, min of curvefit
min_fit_phases, max_fit_phases = self.get_extended_min_max(fit_phaeses)
# determine min of measure phases and min of curvefit
minimum = min(min_phases, min_fit_phases)
# determine max of measured phases and max of curvefit
maximum = max(max_phases, max_fit_phases)
# set axis for phaseplot to min, max values
postplot_obj.ax1[0].set_ylim(minimum, maximum)
# set color of trace for curvefit result to green (trace is visible)
postplot_obj.path_collection_fit.set_color('green')
# set label of trace curvefit results to name of fit function name
postplot_obj.path_collection_fit.set_label('fitted with\n' + func.__name__)
postplot_obj.path_collection_fit.set_offsets(np.c_[time_vals, func(time_vals, *popt)])
else:
# set color of trace for curvefit result to None (trace is invisible)
postplot_obj.path_collection_fit.set_color('None')
# set label of trace curvefit results to '' (no label)
postplot_obj.path_collection_fit.set_label('')
# get legend handles and labels y-axes from subplot magnitude, phase
handles_phase, labels_phase = postplot_obj.ax1[0].get_legend_handles_labels()
handles_mag, labels_mag = postplot_obj.magnitude_axis.get_legend_handles_labels()
handles_equi0, labels_eqi0 = postplot_obj.equi_axis0.get_legend_handles_labels()
handles = handles_phase + handles_mag + handles_equi0
labels = labels_phase + labels_mag + labels_eqi0
# update legend subplot phase, magnitude
postplot_obj.ax1[0].legend(handles, labels, loc = postplot_obj.legend_loc, \
bbox_to_anchor = postplot_obj.legend_bbox_to_anchor)
# refresh plot window
postplot_obj.fig.canvas.draw()
postplot_obj.fig.canvas.flush_events()
# step response of PT1 with initial behavior , Michael
def PT1(self, x, K, T1):
return K * (1 - np.exp(-x/T1)) + self.phase_t0 * np.exp(-x/T1)
# step response of PT2 (D > 1) with initial behavior
def aperiodicPT2(self, x, K, T1, T2,):
return K + self.phase_t0* np.exp(-x/T1)/(T2-T1) - K *T1* np.exp(-x/T1)/(T2-T1) \
- self.phase_t0 * np.exp(-x/T2)/(T2-T1) + K* T2* np.exp(-x/T2)/(T2-T1)
# step response of PT2 (D = 1) with initial behavior, Michael
def aper_borderPT2(self, x, K, T):
return K + self.phase_t0*np.exp(-x/T) - K * np.exp(-x/T) + self.phase_t0*np.exp(-x/T)*x/T + \
- K * np.exp(-x/T) * x/T
# step response of PT2 (0 < D < 1) with initial behavior, Michael
def periodicPT2(self, x, D, T):
return self.K_phases + (self.phase_t0 -self.K_phases)* \
np.exp(-D*x/T)*np.cos(np.sqrt(1-np.square(D))*x/T) + \
(self.phase_t0 - self.K_phases) * D * \
np.exp(-D*x/T)*np.sin(np.sqrt(1-np.square(D))*x/T)/np.sqrt(1-np.square(D))
# step response series of PT2 ( D = 1) and PT1 with initial behavior, Michael
def aper_borderPT2_PT1(self, x, a, b, c, d, T1, T2):
return a + b*np.exp(-x/T1)+c*x*np.exp(-x/T1) + d*np.exp(-x/T2)
# method for cuvefit, Michael
# two different algorithms for curvefit are use depending on used step response for fit, Michael
def curvefit(self, func, time, phases):
if func.__name__ == 'periodicPT2':
popt, pcov = curve_fit(func, time, phases, method = 'trf', \
bounds = ([0.001, 0.001], [0.99, np.inf]))
else:
popt, pcov = curve_fit(func, time, phases, method = 'lm')
return popt, pcov
# method adds the parameter of the fit with the lowest error to annotation string, Michael
def plot_fitted_func_param(self, func, *popt, time_unit):
if func.__name__ == 'PT1':
K = popt[0]
T1 = popt[1]
annotate_string = "K: %.2f\n$T_1$: %.3f %s" %(K, T1, time_unit)
elif func.__name__ == 'aperiodicPT2':
K = popt[0]
T1 = popt[1]
T2 = popt[2]
annotate_string = "K: %.2f\n$T_1$: %.3f %s\n$T_2$: %.3f %s" % (K, T1, time_unit, T2, \
time_unit)
elif func.__name__ == 'aper_borderPT2':
K = popt[0]
T = popt[1]
annotate_string = "K: %.2f\nT: %.3f %s" %(K, T, time_unit)
elif func.__name__ == 'periodicPT2':
K = self.K_phases
D = popt[0]
T = popt[1]
T_2perc = 4*T/D
annotate_string = "K: %.2f\nD: %.3f\nT: %.3f %s\n$T_E$(2%%): %.2f %s" \
% (K, D, T, time_unit, T_2perc, time_unit)
elif func.__name__ == 'aper_borderPT2_PT1':
T1 = popt[4]
T2 = popt[5]
annotate_string = "$T_1$: %.3f %s\n$T_2$: %.3f %s" % (T1, time_unit, T2, time_unit)
else:
# default value
annotate_string =''
return annotate_string
# calculates the fit for all step responses and calulates the lowest error, Michael
# parameter of the plot with the lowest error is returned by this function, Michael
# if curve fit fails for one stept response it takes the next one and plots in command
# window the functions
# which failes, Michael
def choose_fit(self, time, phases):
popt = []
perr = []
used_functions = []
functions = [self.PT1, self.aper_borderPT2, self.aperiodicPT2, self.periodicPT2, \
self.aper_borderPT2_PT1]
for func in functions:
try:
# call curvefit method and put the fitted parameter for step response function
# to a list, Michael
popt.append(self.curvefit(func, time, phases)[0])
# calculation of fitting error
perr.append(np.sqrt(np.square(np.subtract(phases, func(time, *popt[-1]))).mean()))
used_functions.append(func)
except RuntimeError:
print('curvefit of %s fails' %func.__name__)
# get position of parameters in list which has the lowest calculated error, Michael
pos = perr.index(min(perr))
# return function name and parameter with the lowest calculated error during curvefit, Michael
return used_functions[pos], popt[pos]
# scaling time axes depending on the time unit
def scaling_time_axes(self, time_sec, time_unit):
if time_unit == 'min':
time_vals = time_sec/60
elif time_unit == 'hours':
time_vals = time_sec/3600
elif time_unit == 'sec':
time_vals = time_sec
else:
time_vals = time_sec/60
return time_vals
# add 5 % of the distance between min and max to the range
@staticmethod
def get_extended_min_max(array):
distance = array.max() - array.min()
if distance == 0.:
distance = 1
return array.min()-0.05*distance, array.max()+0.05*distance
\ No newline at end of file
No preview for this file type
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment