From 4a206c0eb6623907140c033061f73da987be6df3 Mon Sep 17 00:00:00 2001 From: Markus Rosenstihl Date: Mon, 7 Apr 2014 13:41:39 +0200 Subject: [PATCH] started to decouple fit routine from ui --- QDS.py | 59 +++++++++++++-- data.py | 164 +++++++++++----------------------------- icons/qds_icons.graffle | Bin 14158 -> 51359 bytes libyaff.py | 38 ++++------ mathlib.py | 122 +++++++++++++++++++++--------- 5 files changed, 202 insertions(+), 181 deletions(-) diff --git a/QDS.py b/QDS.py index 4dea6fd..20a17e8 100755 --- a/QDS.py +++ b/QDS.py @@ -9,7 +9,7 @@ from PyQt4.QtCore import * from PyQt4.QtGui import * import matplotlib -from mathlib import fit_anneal, fit_lbfgsb, fit_odr_cmplx, FunctionRegister +from mathlib import fit_anneal, fit_lbfgsb, fit_odr_cmplx, FunctionRegister, FitRoutine matplotlib.use('agg') @@ -87,6 +87,17 @@ class AppWindow(QMainWindow): fit_yaff = QAction("&Fit", self) yaffMenu.addAction(fit_yaff) + self._fit_thread = QThread() +# self._fit_method = FitRoutine() +# self._fit_method.moveToThread(self._fit_thread) +# self._fit_method.finished.connect(self._fit_thread.quit) + +# self._fit_queue = QEventLoop() +# self._fit_timer = QTimer() +# self._fit_timer.setSingleShot(True) +# self._fit_timer.timeout.connect(self._fit_queue.quit) +# self._fit_method.finished.connect(self._fit_queue.quit) + self.signalMapper = QSignalMapper(self) for i, fit_action in enumerate([fit_lmAction, fit_lbfgsbAction, fit_annealAction ]): @@ -284,7 +295,7 @@ class AppWindow(QMainWindow): _yaff.removeObj.connect(self.delParamterObject) gg_y = 10**pos.y() gg_x = 10**pos.x()*2*N.pi - yaff_par = [ gg_y, gg_x , 1.0, 1.0, 0.5, gg_x/10, 1.0, 1.0] + yaff_par = [ gg_y, gg_x , 20.0, 1.0, 0.5, gg_x/100, 1.0, 1.0] _yaff.setParameter(beta=yaff_par) self.parameterWidget.add(_yaff.widget) self.function_registry.register_function(_yaff) @@ -364,7 +375,6 @@ class AppWindow(QMainWindow): self.updatePlot() - def delPeak(self): for i, peak in enumerate(self.peakBoxes.keys()): if peak.widget.isHidden(): @@ -376,6 +386,9 @@ class AppWindow(QMainWindow): self.parameterWidget.vlayout.update() self.updatePlot() + def test(self): + print "test" + def fitData(self, method): fit_methods = [fit_odr_cmplx, fit_lbfgsb, fit_anneal] @@ -387,12 +400,48 @@ class AppWindow(QMainWindow): fixed_params.extend(fcn.getFixed()) _freq, _fit = self.data.get_data() - odr_result = fit_methods[method](_freq, _fit, p0, fixed_params, funcs) + # Prepare + +# self._fit_thread = QThread() + self._fit_method = FitRoutine() + self._fit_method.moveToThread(self._fit_thread) + self._fit_method.finished_fit.connect(self._fit_thread.quit) + + self._fit_method.fit_odr_cmplx(_freq, _fit, p0, fixed_params, funcs) + + self._fit_thread.started.connect(self.test) + + self._fit_thread.started.connect(self._fit_method.fit) + + + self._fit_thread.start() + #self._fit_thread.wait() + + #self._fit_method.moveToThread(self._fit_thread) + #self._fit_thread.start() + + #fit_methods[method](_freq, _fit, p0, fixed_params, funcs) + # Run + + #el = QEventLoop() + #self._fit_thread.finished.connect(el.quit) + #self._fit_method.fit() + import time + time.sleep(1) + # while True: + # time.sleep(0.5) + # print "waiting..." + # if self._fit_thread.isRunning(): + # pass + # else: + odr_result = self._fit_method.result() + # break + print "Set fit data" self.data.set_fit(odr_result.beta, funcs) self.ui.statusbar.showMessage(" ".join(odr_result.stopreason)) result = odr_result.beta - i=0 + i = 0 for fcn in self.function_registry.get_registered_functions(): num_p = len(fcn.getParameter()) beta = odr_result.beta[i:num_p+i] diff --git a/data.py b/data.py index 2db04dc..79c0a9f 100644 --- a/data.py +++ b/data.py @@ -1,17 +1,15 @@ from PyQt4.QtCore import QObject, pyqtSignal, Qt from PyQt4.QtGui import QColor -import numpy as N +import numpy as np import CustomWidgets - import pyqtgraph as pg from PyQt4.QtCore import * from mathlib import id_to_color, hn, FitFunctionCreator, Functions - class Data: - def __init__(self, frequency=N.zeros(1), die_real=N.zeros(1), die_imag=N.zeros(1)): + def __init__(self, frequency=np.zeros(1), die_real=np.zeros(1), die_imag=np.zeros(1)): self.frequency = frequency self.epsilon = die_real + 1j * die_imag self.frequency_fit = frequency @@ -20,12 +18,12 @@ class Data: myPen_real = pg.mkPen(width=3, color=(51,255,127)) #33FF00 - self.data_curve_imag = pg.PlotDataItem(x=[N.nan], y=[N.nan],pen=QColor(0,0,0,0), symbol='o', + self.data_curve_imag = pg.PlotDataItem(x=[np.nan], y=[np.nan],pen=QColor(0,0,0,0), symbol='o', symbolBrush=(255,127,0,127)) - self.data_curve_real = pg.PlotDataItem(x=[N.nan], y=[N.nan],pen=QColor(0,0,0,0), symbol='s', + self.data_curve_real = pg.PlotDataItem(x=[np.nan], y=[np.nan],pen=QColor(0,0,0,0), symbol='s', symbolBrush=(119,202,92,127)) - self.fitted_curve_imag = pg.PlotDataItem(N.array([N.nan]), N.array([N.nan]), pen=myPen_imag) - self.fitted_curve_real = pg.PlotDataItem(N.array([N.nan]), N.array([N.nan]), pen=myPen_real) + self.fitted_curve_imag = pg.PlotDataItem(np.array([np.nan]), np.array([np.nan]), pen=myPen_imag) + self.fitted_curve_real = pg.PlotDataItem(np.array([np.nan]), np.array([np.nan]), pen=myPen_real) self.length = len(frequency) self.meta = dict() self.fit_limits = [frequency.min(), frequency.max(), die_imag.min(), die_imag.max()] @@ -41,11 +39,6 @@ class Data: fit_real, fit_imag = FitFunctionCreator().fitfcn(param, self.frequency_fit, *funcs) self.epsilon_fit = fit_real + 1j*fit_imag - def __del__(self): - #self.remove_curves() - pass - #def set_fit_limits(self, limits=(None,None,None,None)): - def set_data(self,f,e_real,e_imag): self.frequency = f self.epsilon = e_real + 1j*e_imag @@ -63,15 +56,10 @@ class Data: self.fit_limits[2] = ymin self.fit_limits[3] = ymax - def get_data(self): - """ - - """ - mask = N.ones(len(self.frequency), dtype='bool') + #mask = np.ones(len(self.frequency), dtype='bool') mask = (self.frequency > self.fit_limits[0]) & (self.frequency < self.fit_limits[1]) #mask &= (self.epsilon.imag > self.fit_limits[2]) & (self.epsilon.imag < self.fit_limits[3]) - #mask &= (self.epsilon.real > self.fit_limits[2]) & (self.epsilon.real < self.fit_limits[3]) return self.frequency[mask], self.epsilon[mask] def remove_curves(self): @@ -80,7 +68,6 @@ class Data: print "remove fitted_curve" #if self.fitted_curve is not None: self.fitted_curve.remove() - class BaseObject(QObject): changedData = pyqtSignal() removeObj = pyqtSignal(QObject) @@ -91,19 +78,37 @@ class BaseObject(QObject): myPen = pg.mkPen( style=Qt.DotLine, width=2.5) - self.data_curve_real = pg.PlotDataItem(x=N.array([N.nan]),y=N.array([N.nan]), pen=myPen) + self.data_curve_real = pg.PlotDataItem(x=np.array([np.nan]),y=np.array([np.nan]), pen=myPen) self.plt_real = plt_real self.plt_real.addItem(self.data_curve_real) - self.data_curve_imag = pg.PlotDataItem(x=N.array([N.nan]),y=N.array([N.nan]), pen=myPen) + self.data_curve_imag = pg.PlotDataItem(x=np.array([np.nan]),y=np.array([np.nan]), pen=myPen) self.plt_imag = plt_imag self.plt_imag.addItem(self.data_curve_imag) self.limits = limits - self.f=Functions() - + # private varaibles + self.functions = Functions() + #self.functions.step_signal.connect(self.test) # TODO self._color = QColor("white") + self._id = None + self._widget = None + self._frequency = np.logspace(np.log10(limits[0]), np.log10(limits[1]), 256) + self._data = None + self._func = None + + def test(self,p): + print "found:",p + + @property + def id_string(self): + return self._id + + @id_string.setter + def id_string(self, id): + self._func = self.functions.get_function(id) + self._id = id @property def color(self): @@ -115,6 +120,15 @@ class BaseObject(QObject): self.data_curve_real.setPen(c) self.data_curve_imag.setPen(c) + @property + def widget(self): + return self._widget + + @widget.setter + def widget(self, wdgt): + self._widget = wdgt + self._widget.changedTable.connect(self.updateData) + self._widget.removeMe.connect(self.removeMe) def getParameter(self): p = self.widget.getTable() @@ -128,12 +142,8 @@ class BaseObject(QObject): self.widget.updateTable(beta, sd_beta) self.updateData() - def get_data(self): - return self.frequency, self.conductivity - - def get_epsilon_static(self): - return self.frequency, self.epsilon_static + return self._frequency, self._data def removeMe(self): self.plt_imag.removeItem(self.data_curve_imag) @@ -141,130 +151,46 @@ class BaseObject(QObject): self.removeObj.emit(self) self.changedData.emit() + def updateData(self): + self._data = self._func(self.getParameter(), self._frequency) + self.data_curve_real.setData(x=self._frequency, y=self._data[0]) + self.data_curve_imag.setData(x=self._frequency, y=self._data[1]) + self.changedData.emit() class Conductivity(BaseObject): def __init__( self, plt_imag=None, plt_real=None, limits=None ): super(Conductivity, self).__init__(plt_real=plt_real, plt_imag=plt_imag, limits=limits) self.widget = CustomWidgets.ConductivityWidget() - self.widget.changedTable.connect(self.updateData) - self.widget.removeMe.connect(self.removeMe) - self.color = QColor("blue") - - self.frequency = None - self.conductivity = None - self.id_string = "conductivity" - - - def updateData(self): - # get current axis limits - x_min, x_max, y_min, y_max = self.limits - self.frequency = N.logspace(N.log10(x_min), N.log10(x_max), 254) - p = self.getParameter() - - self.conductivity = self.f.cond_cmplx(p, self.frequency) - - self.data_curve_real.setData(x=self.frequency, y=self.conductivity[0], label="Cond.") - self.data_curve_imag.setData(x=self.frequency, y=self.conductivity[1], label="Cond.") - self.changedData.emit() - - - class PowerComplex(BaseObject): def __init__( self, plt_real=None, plt_imag=None, limits=None ): super(PowerComplex, self).__init__(plt_real=plt_real, plt_imag=plt_imag, limits=limits) self.widget = CustomWidgets.PowerLawWidget() - self.widget.changedTable.connect(self.updateData) - self.widget.removeMe.connect(self.removeMe) - - self.frequency = None - self.powerlaw = None - self.color = QColor("#ff44c4") self.id_string = 'power' - def updateData(self): - # get current axis limits - x_min, x_max, y_min, y_max = self.limits - - p = self.getParameter() - - self.frequency = N.logspace(N.log10(x_min), N.log10(x_max), 1024) - self.powerlaw = self.f.power_cmplx(p, self.frequency) # imaginary part - self.data_curve_real.setData(x=self.frequency, y=self.powerlaw[0], label="Power Law") - self.data_curve_imag.setData(x=self.frequency, y=self.powerlaw[1], label="Power Law") - self.changedData.emit() - - - class Static(BaseObject): def __init__( self, plt_real=None, plt_imag=None, limits=None ): super(Static, self).__init__(plt_real=plt_real, plt_imag=plt_imag, limits=limits) - self.widget = CustomWidgets.StaticWidget() - self.widget.changedTable.connect(self.updateData) - self.widget.removeMe.connect(self.removeMe) - - self.frequency = None - self.static = None - self.color = QColor('#FF0F13') self.id_string = 'static' - - def updateData(self): - # get current axis limits - x_min, x_max, y_min, y_max = self.limits - p = self.getParameter() - self.frequency = N.logspace(N.log10(x_min), N.log10(x_max), 1024) - self.static = self.f.static_cmplx(p, self.frequency) [0] # real part - self.data_curve_real.setData(x=self.frequency, y=self.static, label="Power Law") - self.changedData.emit() - - - class Peak(BaseObject): def __init__( self, id_num=None, plt_real=None, plt_imag=None, limits=None ): super(Peak, self).__init__(plt_real=plt_real, plt_imag=plt_imag, limits=limits) self.widget = CustomWidgets.PeakWidget() + self.widget.setColor(self.color) self.widget.setId(id_num) self.color = id_to_color(id_num) self.id_num = id_num - self.widget.setColor(self.color) - self.widget.changedTable.connect(self.updateData) - self.widget.removeMe.connect(self.removeMe) - self.frequency = None - self.epsilon = None self.id_string = "hn" - def updateData(self): - x_min,x_max, y_min, y_max = self.limits - self.frequency = N.logspace(N.log10(x_min), N.log10(x_max), 1024) - self.epsilon = self.f.hn_cmplx(self.getParameter(), self.frequency) - self.data_curve_imag.setData(x=self.frequency, y=self.epsilon[1]) - self.data_curve_real.setData(x=self.frequency, y=self.epsilon[0]) - self.changedData.emit() - - class YAFF(BaseObject): def __init__( self, plt_real=None, plt_imag=None, limits=None ): super(YAFF, self).__init__(plt_real=plt_real, plt_imag=plt_imag, limits=limits) self.widget = CustomWidgets.YaffWidget() - self.widget.changedTable.connect(self.updatePeak) - self.widget.removeMe.connect(self.removeMe) - self.frequency = None - self.epsilon = None self.id_string = "yaff" - - def updatePeak(self): - x_min,x_max, y_min, y_max = self.limits - self.frequency = N.logspace(N.log10(x_min), N.log10(x_max), 1024) - self.epsilon = self.f.hn_cmplx(self.getParameter()[:4], self.frequency) - self.data_curve_imag.setData(x=self.frequency, y=self.epsilon[1]) - self.data_curve_real.setData(x=self.frequency, y=self.epsilon[0]) - self.changedData.emit() - - diff --git a/icons/qds_icons.graffle b/icons/qds_icons.graffle index ed2ae80dd0ccb23eb62a249ba29f0616efacd033..14b45c4413331a17be1ac4c1a6c3e269df70fd98 100644 GIT binary patch literal 51359 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≻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 literal 14158 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 roNXHTtlsOugo}}Cne^T;z@qDwzvH^*)d!! diff --git a/libyaff.py b/libyaff.py index 4210ced..49c3e9a 100644 --- a/libyaff.py +++ b/libyaff.py @@ -3,37 +3,35 @@ __author__ = 'markusro' from numpy import * import mathlib +from PyQt4.QtCore import pyqtSignal, QObject + import scipy.special import scipy.integrate - #define norm1(a,b) exp(gammln(b/a))/(a*pow(b/a,b/a)) - #define glntau1(x,t,a,b) exp( -exp( (log(x)-log(t))*a )*b/a) * pow(x/t,b) def filon(oms, unsorted_x,unsorted_y): x = unsorted_x[unsorted_x.argsort()] y = unsorted_y[unsorted_x.argsort()] - - amps = zeros(oms.size, dtype='complex') for i,om in enumerate(oms): - amps[i] = sum(diff(y)/diff(x)*(cos(om*x[1:])-cos(om*x[:-1])))/om**2 - amps[i] += 1j*(y[0]/om + sum(diff(y)/diff(x)*(sin(om*x[1:])-sin(om*x[:-1])))/om**2) + amps[i] = sum(diff(y)/diff(x)*(cos(om*x[1:])-cos(om*x[:-1])))/om**2 + amps[i] += 1j*( - sum(diff(y)/diff(x)*(sin(om*x[1:])-sin(om*x[:-1])))/om**2) + #-y[0]/om return amps - -class Yaff: +class Yaff(QObject): + step_signal = pyqtSignal(list) def __init__(self, dist_type=0): + super(Yaff,self).__init__() self.dist_x = logspace(-10,5,512) self.dist_y = zeros(self.dist_x.size) self.dist_type = [self.yaff, self.yaff_gg, ][dist_type] - - def gg(self, p, tau): tau0, a, b = p """ @@ -44,14 +42,12 @@ class Yaff: g = exp(-b/a*exp( (log(tau)-log(tau0))*a))*(tau/tau0)**b return g*NGG - def ggb(self, p, tau): tau0,a,b = p norm_ggb = a*(1+b)/pi *b**(b/(1+b)) * sin(pi*b/(1+b)) g = (b*exp( (log(tau)-log(tau0)) *a) + exp( (log(tau)-log(tau0))*(-a*b)))**(-1) return norm_ggb*g - def gg_hw(self, p ,tau): tau0, a, b, g, s = p """ @@ -63,7 +59,6 @@ class Yaff: g = exp(-b/a*exp((log(tau) - log(tau0))*a))*(tau/tau0)**b * (1 + (tau*s/tau0)**(g-b)) return g*NGG - def dist_to_eps(omegas,dist_x,dist_y): epp = zeros(len(omegas), dtype='complex') for i,om in enumerate(omegas): @@ -79,13 +74,11 @@ class Yaff: phi[i] = self.phi_t(t,dist_x,dist_y) return phi - def phi_t(self, t, dist_x, dist_y): kern = dist_y*exp(-t/dist_x) phi = scipy.integrate.simps(kern,log(dist_x)) return phi - def williams_watts(self, phi_1, phi_2, lambd): return phi_1 * ( (1-lambd) + lambd*phi_2) @@ -114,19 +107,21 @@ class Yaff: """ delta_eps, tau1, a1, b1, lambd, tau2, a2, b2 = p - dist_ggb = self.ggb(p=[tau2,a2,b2], tau=self.dist_x) - dist_gg = self.gg(p=[tau1,a1,b1], tau=self.dist_x) + dist_ggb = self.ggb(p=[tau2,a2,b2], tau=self.dist_x) + dist_gg = self.gg (p=[tau1,a1,b1], tau=self.dist_x) ts = logspace(-10,5,768) phi_beta = self.dist_to_relax(ts, self.dist_x, dist_ggb).real phi_alpha = self.dist_to_relax(ts, self.dist_x, dist_gg).real - # William-Watts-Ansatz - phi_tot = (1-lambd) + lambd*phi_beta - phi_tot *= phi_alpha - epp = delta_eps*2*pi*x*filon(2*pi*x, ts, phi_tot ) + # William-Watts-Ansatz + phi_tot = (1-lambd) + lambd*phi_beta + phi_tot *= phi_alpha + epp = delta_eps*2*pi*x*filon(2*pi*x, ts, phi_tot) + self.step_signal.emit(list(p)) if cb != None: cb.next(p,x,epp) + return epp def yaff_gg(self, p,x, cb=None): @@ -145,7 +140,6 @@ class Yaff: cb(p,x,epp) return epp - def show_correlations(cov, ax): cor = zeros(cov.shape) for i in range(cor.shape[0]): diff --git a/mathlib.py b/mathlib.py index d1df71f..d98ba83 100644 --- a/mathlib.py +++ b/mathlib.py @@ -2,8 +2,10 @@ __author__ = 'markusro' from PyQt4.QtGui import QColor +from PyQt4.QtCore import QObject,pyqtSignal,QThread -import numpy as N + +import numpy as np from scipy import optimize as opt, odr import libyaff @@ -74,12 +76,12 @@ def fit_lbfgsb(x, y, p0, fixed, funcs): def hn(p, nu): delta_eps, tau, a, b = p - om = 2 * N.pi * nu - Phi = N.arctan((om * tau) ** a * N.sin(N.pi * a / 2.) / (1. + (om * tau) ** a * N.cos(N.pi * a / 2.))) - e_loss = delta_eps * (1 + 2 * (om * tau) ** a * N.cos(N.pi * a / 2.) + (om * tau) ** (2. * a) ) ** ( - -b / 2.) * N.sin(b * Phi) - e_stor = delta_eps * (1 + 2 * (om * tau) ** a * N.cos(N.pi * a / 2.) + (om * tau) ** (2. * a) ) ** ( - -b / 2.) * N.cos(b * Phi) + om = 2 * np.pi * nu + Phi = np.arctan((om * tau) ** a * np.sin(np.pi * a / 2.) / (1. + (om * tau) ** a * np.cos(np.pi * a / 2.))) + e_loss = delta_eps * (1 + 2 * (om * tau) ** a * np.cos(np.pi * a / 2.) + (om * tau) ** (2. * a) ) ** ( + -b / 2.) * np.sin(b * Phi) + e_stor = delta_eps * (1 + 2 * (om * tau) ** a * np.cos(np.pi * a / 2.) + (om * tau) ** (2. * a) ) ** ( + -b / 2.) * np.cos(b * Phi) return e_loss # 2* oder nicht? @@ -101,13 +103,13 @@ def mini_func(p, x, y): res = y - multi_hn(p, x) # apply weights res /= 1 / y - return N.sqrt(N.dot(res, res)) + return np.sqrt(np.dot(res, res)) def multi_hn(p, nu): conductivity = p[1] cond_beta = p[2] - om = 2 * N.pi * nu + om = 2 * np.pi * nu e_loss = conductivity / om ** cond_beta e_loss += p[0] #for key, igroup in groupby(p[3:], lambda x: x//4): @@ -117,71 +119,77 @@ def multi_hn(p, nu): #print delta_eps,tau,a,b #a = 0.5 *(1 + N.tanh(a)) #b = 0.5 *(1 + N.tanh(b)) - Phi = N.arctan((om * tau) ** a * N.sin(N.pi * a / 2.) / (1. + (om * tau) ** a * N.cos(N.pi * a / 2.))) - e_loss += 2 * delta_eps * (1 + 2 * (om * tau) ** a * N.cos(N.pi * a / 2.) + (om * tau) ** (2. * a) ) ** ( - -b / 2.) * N.sin(b * Phi) + Phi = np.arctan((om * tau) ** a * np.sin(np.pi * a / 2.) / (1. + (om * tau) ** a * np.cos(np.pi * a / 2.))) + e_loss += 2 * delta_eps * (1 + 2 * (om * tau) ** a * np.cos(np.pi * a / 2.) + (om * tau) ** (2. * a) ) ** ( + -b / 2.) * np.sin(b * Phi) #e_stor = delta_eps * (1+ 2*(om*tau)**a * N.cos(N.pi*a/2.) + (om*tau)**(2.*a) )**(-b/2.)*N.cos(b*Phi) return e_loss def tau_peak(f, a, b): - tau = (N.sin(N.pi * a / 2. / (b + 1)) / N.sin(N.pi * a * b / 2. / (b + 1))) ** (1 / a) - tau /= 2 * N.pi * f + tau = (np.sin(np.pi * a / 2. / (b + 1)) / np.sin(np.pi * a * b / 2. / (b + 1))) ** (1 / a) + tau /= 2 * np.pi * f return tau ### define funcs here -class Functions: +class Functions(QObject): + step_signal = pyqtSignal(list) def __init__(self): + super(Functions,self).__init__() self.list = { # provides functions: "id_string":(function, number_of_parameters) - "hn":(self.hn_cmplx,4), - "conductivity":(self.cond_cmplx,3), - "power":(self.power_cmplx,2), - "static":(self.static_cmplx,1), - "yaff":(self.yaff,8) + "hn":(self.hn_cmplx, 4), + "conductivity":(self.cond_cmplx, 3), + "power":(self.power_cmplx, 2), + "static":(self.static_cmplx, 1), + "yaff":(self.yaff, 8) } self.YAFF = libyaff.Yaff() def hn_cmplx(self, p, x): - om = 2*N.pi*x + om = 2*np.pi*x #hn = om*1j eps,t,a,b = p hn = eps/(1+(1j*om*t)**a)**b - cplx = N.array([hn.real, -hn.imag]) + cplx = np.array([hn.real, -hn.imag]) return cplx def cond_cmplx(self, p, x): - om = 2*N.pi*x + om = 2*np.pi*x sgma, isgma, n = p cond = sgma/(om**n) + isgma/(1j*om**n)# Jonscher (Universal Dielectric Response: e",e' prop sigma/omega**n - cplx = N.array([cond.real, -cond.imag]) + cplx = np.array([cond.real, -cond.imag]) + self.step_signal.emit(list(p)) return cplx def power_cmplx(self, p, x): - om = 2*N.pi*x + om = 2*np.pi*x sgma,n = p power = sgma/(om*1j)**n - cplx = N.array([power.real, -power.imag]) + cplx = np.array([power.real, -power.imag]) return cplx def static_cmplx(self, p, x): eps_inf = p[0] - static = N.ones( (2,x.size) )*eps_inf + static = np.ones( (2,x.size) )*eps_inf static[1,:] *= 0 # set imag part zero #cplx = N.array([static.real, static.imag]) return static def yaff(self,p,x): - ya = self.YAFF.yaff(p,x) - cplx = N.array([ya.imag[::-1], ya.real]) + ya = self.YAFF.yaff(p[:8],x) + cplx = np.array([ya.imag, ya.real]) + self.step_signal.emit(list(p)) return cplx def get(self,name): return self.list[name] + def get_function(self,name): + return self.list[name][0] class FitFunctionCreator: def __init__(self): @@ -190,9 +198,9 @@ class FitFunctionCreator: def fitfcn(self, p0, x, *funcs): if x.ndim == 2: - self.data = N.zeros( x.shape ) + self.data = np.zeros( x.shape ) else: - self.data = N.zeros( (2,x.size) ) + self.data = np.zeros( (2,x.size) ) ndx = 0 for fn in funcs: # loop over functions and add the results f,num_p = self.functions.get(fn) @@ -204,15 +212,16 @@ class FitFunctionCreator: ndx += num_p return self.data + def fit_odr_cmplx(x, y, p0, fixed, fcns): f = FitFunctionCreator() #if x.ndim < 2: # x = N.resize(x, (2,x.size)) - if N.iscomplexobj(y) and y.ndim == 1: - weights = 1/N.abs(y)**2 - we = N.resize(weights, (2, weights.size)) + if np.iscomplexobj(y) and y.ndim == 1: + weights = 1/np.abs(y)**2 + we = np.resize(weights, (2, weights.size)) #we = 1/N.array([y.real**2, y.imag**2]) - y = N.array([y.real, y.imag]) + y = np.array([y.real, y.imag]) else: raise NotImplementedError, "need complex input for now" dat = odr.Data(x, y, we=we) @@ -222,6 +231,49 @@ def fit_odr_cmplx(x, y, p0, fixed, fcns): #print fit.output.pprint() return fit.output +class FitRoutine(QObject): + finished_fit = pyqtSignal() + data_ready = pyqtSignal([]) + def __init__(self): + super(FitRoutine,self).__init__() + self.f = FitFunctionCreator() + self._fitter = self.fit_odr_cmplx + self._odr_fit = None + + @property + def fitter(self): + return self._fitter + + @fitter.setter + def fitter(self,f): + self._fitter = f + + + def fit_odr_cmplx(self, x, y, p0, fixed, fcns): + #if x.ndim < 2: + # x = N.resize(x, (2,x.size)) + if np.iscomplexobj(y) and y.ndim == 1: + weights = 1/np.abs(y)**2 + we = np.resize(weights, (2, weights.size)) + #we = 1/N.array([y.real**2, y.imag**2]) + y = np.array([y.real, y.imag]) + else: + raise NotImplementedError, "need complex input for now" + + dat = odr.Data(x, y, we=we) + mod = odr.Model(self.f.fitfcn, extra_args=fcns) + self._odr_fit = odr.ODR(dat, mod, p0, ifixx=(0,), ifixb=fixed, maxit=10) + + def fit(self): + #print "TID in FitRoutine", QThread.thread() + self._odr_fit.run() + print "emit finished" + self.finished_fit.emit() + + def result(self): + return self._odr_fit.output + + class FunctionRegister: def __init__(self):