qdsfit/data.py

267 lines
9.0 KiB
Python

from PyQt4.QtGui import QColor
import numpy as np
import pyqtgraph as pg
from PyQt4.QtCore import *
import CustomWidgets
from mathlib import id_to_color, FitFunctionCreator, Functions
import libyaff
class Data:
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
self.epsilon_fit = die_real*0 + 1j * die_imag*0
myPen_imag = pg.mkPen(width=3, color=(255,255,127))
myPen_real = pg.mkPen(width=3, color=(51,255,127))
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=[np.nan], y=[np.nan],pen=QColor(0,0,0,0), symbol='s',
symbolBrush=(119,202,92,127))
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()]
self.fit_param = None
self.fit_funcs = None # list of fit functions
self.hide_funcs = None # remove these func from the data
def set_fit(self, param, funcs):
self.fit_funcs = funcs
self.hide_funcs = []
self.fit_param = param
fit_real, fit_imag = FitFunctionCreator().fitfcn(param, self.frequency_fit, *funcs)
self.epsilon_fit = fit_real + 1j*fit_imag
def set_data(self,f,e_real,e_imag):
self.frequency = f
self.epsilon = e_real + 1j*e_imag
self.epsilon_fit = 0*e_real + 1j*e_imag*0
self.fit_limits = [f.min(), f.max(), e_imag.min(), e_imag.max()]
self.data_curve_imag.setData(f,e_imag)
self.data_curve_real.setData(f,e_real)
def set_fit_xlimits(self, xmin, xmax):
self.fit_limits[0] = xmin
self.fit_limits[1] = xmax
self.frequency_fit = self.frequency[(self.frequency <= xmax) & (self.frequency >= xmin)]
def set_fit_ylimits(self, ymin, ymax):
self.fit_limits[2] = ymin
self.fit_limits[3] = ymax
def get_data(self):
#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])
return self.frequency[mask], self.epsilon[mask]
def remove_curves(self):
print "remove data_curve"
#if self.data_curve is not None: self.data_curve.remove()
print "remove fitted_curve"
#if self.fitted_curve is not None: self.fitted_curve.remove()
class BaseObject(QObject):
changedData = pyqtSignal()
removeObj = pyqtSignal(QObject)
def __init__(self, plt_real=None, plt_imag=None, limits=None):
super(BaseObject, self).__init__()
myPen = pg.mkPen( style=Qt.DotLine,
width=2.5)
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=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
# private varaibles
self.functions = Functions()
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
self._beta = None
self._sd_beta = None
self._param_number = 0
@property
def param_number(self):
return self._param_number
@param_number.setter
def param_number(self, num):
self._param_number = num
@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):
return self._color
@color.setter
def color(self, c):
self._color = c
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) # TODO better to use self.setParameter
self._widget.removeMe.connect(self.removeMe)
def getParameter(self):
p = self.widget.getTable() # TODO ugly ... should return self._beta etc ...?
return p
def getFixed(self):
p = self.widget.fixedParameter()
return p
def setParameter(self, beta, sd_beta=None):
self._beta = beta
self._sd_beta = sd_beta
self.widget.updateTable(beta, sd_beta)
self.updateData()
def get_data(self):
return self._frequency, self._data
def removeMe(self):
self.plt_imag.removeItem(self.data_curve_imag)
self.plt_real.removeItem(self.data_curve_real)
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()
def function(self,p,x):
raise NotImplementedError, "This needs to be implemented in your subclass"
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.color = QColor("blue")
self.id_string = "conductivity"
self.param_number = 3
def function(self, p, 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 = np.array([cond.real, -cond.imag])
return cplx
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.color = QColor("#ff44c4")
self.id_string = 'power'
self.param_number = 2
def function( self, p, x ):
om = 2*np.pi*x
sgma,n = p
power = sgma/(om*1j)**n
cplx = np.array([power.real, -power.imag])
return cplx
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.color = QColor('#FF0F13')
self.id_string = 'static'
self.param_number = 1
def function( self, p, x ):
eps_inf = p[0]
static = np.ones( (2,x.size) )*eps_inf
static[1,:] *= 0 # set imag part zero
return static
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.setId(id_num)
self.color = id_to_color(id_num)
self.widget.setColor(self.color)
self.id_num = id_num
self.id_string = "hn"
self.param_number = 4
def function( self, p, x ):
eps,t,a,b = p
om = 2*np.pi*x
hn = eps/(1+(1j*om*t)**a)**b
cplx = np.array([hn.real, -hn.imag])
return cplx
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.on_model_changed.connect(self.change_model)
self.id_string = "yaff"
self._libyaff = libyaff.Yaff(self.widget.getYaffType())
self._param_number = self._libyaff.params
@property
def param_number(self):
return self._param_number
@param_number.setter
def param_number(self, num=None):
self._param_number = self._libyaff.params
def change_model(self):
self._libyaff = libyaff.Yaff(self.widget.getYaffType())
self.param_number = self._libyaff.params
self.updateData()
def function( self, p, x ):
ya = self._libyaff.loss( p[:self.param_number], x)
cplx = np.array([ya.imag, ya.real])
return cplx