def saveMA(self, fileName=None): if self.imgData is None: raise HelpfulException("There is no processed data to save.") if fileName is None: dh = self.getElement("File Loader").baseDir().name() self.fileDialog = FileDialog(None, "Save image data", dh, '*.ma') self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.fileDialog.show() self.fileDialog.fileSelected.connect(self.saveMA) return table = self.dbquery.table() x = table['xPos'].min() y = table['yPos'].min() #print "params:", self.imgData.dtype.names #print "shape:", self.imgData.shape #arr = MetaArray(self.currentData) ### need to format this with axes and info arr = MetaArray([self.imgData[p] for p in self.imgData.dtype.names], info=[ {'name':'vals', 'cols':[{'name':p} for p in self.imgData.dtype.names]}, {'name':'xPos', 'units':'m', 'values':np.arange(self.imgData.shape[0])*self.spacing+x}, {'name':'yPos', 'units':'m', 'values':np.arange(self.imgData.shape[1])*self.spacing+y}, {'spacing':self.spacing} ]) arr.write(fileName)
def saveMA(self, fileName=None): if fileName is None: dh = self.getElement("File Loader").baseDir().name() self.fileDialog = FileDialog(None, "Save traces", dh, '*.ma') self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.fileDialog.show() self.fileDialog.fileSelected.connect(self.saveMA) return #arr = MetaArray(self.currentData) ### need to format this with axes and info arr = MetaArray([ self.currentData['Rs'], self.currentData['Rm'], self.currentData['Ih'] ], info=[{ 'name': 'vals', 'cols': [{ 'name': 'Rs', 'units': 'Ohms' }, { 'name': 'Rm', 'units': 'Ohms' }, { 'name': 'Ih', 'units': 'A' }] }, { 'name': 'time', 'units': 's', 'values': self.currentData['time'] }]) arr.write(fileName)
def saveMA(self, fileName=None): if fileName is None: dh = self.getElement("File Loader").baseDir().name() self.fileDialog = FileDialog(None, "Save traces", dh, '*.ma') self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.fileDialog.show() self.fileDialog.fileSelected.connect(self.saveMA) return #arr = MetaArray(self.currentData) ### need to format this with axes and info arr = MetaArray([self.currentData['Rs'], self.currentData['Rm'], self.currentData['Ih']], info=[ {'name':'vals', 'cols':[ {'name':'Rs', 'units':'Ohms'}, {'name':'Rm', 'units':'Ohms'}, {'name':'Ih', 'units':'A'}]}, {'name':'time', 'units':'s', 'values':self.currentData['time']}]) arr.write(fileName)
class MapImager(AnalysisModule): def __init__(self, host): AnalysisModule.__init__(self, host) self.dbIdentity = 'MapImager' ## how we identify to the database; this determines which tables we own modPath = os.path.abspath(os.path.split(__file__)[0]) self._elements_ = OrderedDict([ ('Database Query', {'type':'ctrl', 'object': DatabaseQueryWidget(self.dataManager()), 'size':(300,200), 'host':self}), ('File Loader', {'type':'fileInput', 'pos':('below', 'Database Query'), 'host':self, 'showFileTree':False}), ('Color Mapper', {'type':'ctrl', 'object': MapImagerColorMapper(filePath=os.path.join(modPath, "colorMaps"), host=self), 'size': (200,300), 'pos':('right', 'Database Query')}), ('Contour Plotter', {'type':'ctrl', 'object':ContourPlotter(host=self), 'pos':('below', 'Color Mapper')}), ('Canvas', {'type': 'canvas', 'pos': ('bottom', 'Color Mapper'), 'size': (700,600), 'allowTransforms': False, 'hideCtrl': True, 'args': {'name': 'MapImager'}}), ('Map Convolver', {'type':'ctrl', 'object': MapConvolver(), 'size': (200, 200), 'pos':('bottom', 'File Loader')}), ('Spatial Correlator', {'type':'ctrl', 'object':SpatialCorrelator(), 'size':(100,100), 'pos': ('bottom', 'Map Convolver')}) #('File Loader', {'type': 'fileInput', 'size': (100, 300), 'host': self, 'args': {'showFileTree': True}}), #('ctrl', {'type': 'ctrl', 'object': self.ctrlWidget, 'pos': ('bottom', 'File Loader'), 'size': (100, 100)}), #('Rs Plot', {'type': 'plot', 'pos':('right', 'File Loader'), 'size':(200, 600), 'labels':{'left':(None,'Ohms'), 'bottom':(None,'s')}}), #('Rm Plot', {'type': 'plot', 'pos':('bottom', 'Rs Plot'), 'size':(200, 600),'labels':{'left':(None,'Ohms'), 'bottom':(None,'s')}}), #('Ih Plot', {'type': 'plot', 'pos':('bottom', 'Rm Plot'), 'size':(200, 600), 'labels':{'left':(None,'A'), 'bottom':(None, 's')}}), #('Traces Plot', {'type': 'plot', 'pos':('right', 'ctrl'), 'size':(200, 600), 'labels':{'left':(None,'A'), 'bottom':(None,'s')}}), ]) self.initializeElements() for el in self.getAllElements(): self.getElement(el, create=True) ## reserve variables that will get set later self.imgItem = None self.spacing = None self.imgData = None self.dbquery = self.getElement("Database Query") self.canvas = self.getElement("Canvas") self.mapConvolver = self.getElement("Map Convolver") self.colorMapper = self.getElement("Color Mapper") self.spatialCorrelator = self.getElement("Spatial Correlator") self.contourPlotter = self.getElement("Contour Plotter") self.contourPlotter.setCanvas(self.canvas) #self.outline = self.spatialCorrelator.getOutline() #self.canvas.addGraphicsItem(self.outline) self.dbquery.sigTableChanged.connect(self.setData) self.mapConvolver.sigOutputChanged.connect(self.convolverOutputChanged) self.mapConvolver.sigFieldsChanged.connect(self.convolverFieldsChanged) self.spatialCorrelator.sigOutputChanged.connect(self.correlatorOutputChanged) self.colorMapper.sigChanged.connect(self.computeColors) def getFields(self): return self.mapConvolver.getFields() def setData(self): data = self.dbquery.table() self.data = data self.getElement("Spatial Correlator").setData(data) #self.getElement("Map Convolver").setData(data) def convolverOutputChanged(self, data, spacing): self.spacing = spacing self.imgData = data self.recolorMap(self.colorMapper.getColorArray(data)) self.adjustContours(data, self.imgItem) def computeColors(self): if self.imgData is not None: try: self.recolorMap(self.colorMapper.getColorArray(self.imgData)) except ValueError: self.mapConvolver.process() def correlatorOutputChanged(self, data): #newFields= [f for f in data.dtype.descr if f not in self.data.dtype.descr] #if len(newFields) > 0: #arr = np.zeros(len(data), dtype=self.data.dtype.descr+newFields) #arr[:] = self.data #arr[:] = data #self.data = arr self.data = data self.mapConvolver.setData(self.data) def adjustContours(self, data, parentItem=None): if data is None: return self.contourPlotter.adjustContours(data, parentItem=self.imgItem) def recolorMap(self, data): if self.imgItem is None: if data is None: return table = self.dbquery.table() x = table['xPos'].min() y = table['yPos'].min() self.imgItem = ImageCanvasItem(data, pos=(x, y), scale=self.spacing, movable=False, scalable=False, name="ConvolvedMap") self.canvas.addItem(self.imgItem) return self.imgItem.updateImage(data) def convolverFieldsChanged(self, fields): self.giveOptsToCM(fields) def giveOptsToCM(self, fields): self.colorMapper.setArgList(fields) self.contourPlotter.setArgList(fields) def saveMA(self, fileName=None): if self.imgData is None: raise HelpfulException("There is no processed data to save.") if fileName is None: dh = self.getElement("File Loader").baseDir().name() self.fileDialog = FileDialog(None, "Save image data", dh, '*.ma') self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.fileDialog.show() self.fileDialog.fileSelected.connect(self.saveMA) return table = self.dbquery.table() x = table['xPos'].min() y = table['yPos'].min() #print "params:", self.imgData.dtype.names #print "shape:", self.imgData.shape #arr = MetaArray(self.currentData) ### need to format this with axes and info arr = MetaArray([self.imgData[p] for p in self.imgData.dtype.names], info=[ {'name':'vals', 'cols':[{'name':p} for p in self.imgData.dtype.names]}, {'name':'xPos', 'units':'m', 'values':np.arange(self.imgData.shape[0])*self.spacing+x}, {'name':'yPos', 'units':'m', 'values':np.arange(self.imgData.shape[1])*self.spacing+y}, {'spacing':self.spacing} ]) arr.write(fileName) def loadFileRequested(self, fhList): canvas = self.getElement('Canvas') model = self.dataModel for fh in fhList: try: ## TODO: use more clever detection of Scan data here. if fh.isFile() or model.dirType(fh) == 'Cell': canvas.addFile(fh) else: #self.loadScan(fh) debug.printExc("MapAnalyzer does not yet support loading scans") return False return True except: debug.printExc("Error loading file %s" % fh.name()) return False
class CellHealthTracker(AnalysisModule): def __init__(self, host): AnalysisModule.__init__(self, host) self.ctrlWidget = QtGui.QWidget() self.ctrl = CellHealthCtrlTemplate.Ui_widget() self.ctrl.setupUi(self.ctrlWidget) self.ctrlStateGroup = pg.WidgetGroup(self.ctrlWidget) self.ctrl.startSpin.setOpts(step=0.05, suffix='s', siPrefix=True, value=0.35, dec=False) self.ctrl.stopSpin.setOpts(step=0.05, suffix='s', siPrefix=True, value=0.5, dec=False) ## Setup basic GUI self._elements_ = OrderedDict([ ('File Loader', {'type': 'fileInput', 'size': (100, 300), 'host': self, 'args': {'showFileTree': True}}), ('ctrl', {'type': 'ctrl', 'object': self.ctrlWidget, 'pos': ('bottom', 'File Loader'), 'size': (100, 100)}), ('Rs Plot', {'type': 'plot', 'pos':('right', 'File Loader'), 'size':(200, 600), 'labels':{'left':(None,'Ohms'), 'bottom':(None,'s')}}), ('Rm Plot', {'type': 'plot', 'pos':('bottom', 'Rs Plot'), 'size':(200, 600),'labels':{'left':(None,'Ohms'), 'bottom':(None,'s')}}), ('Ih Plot', {'type': 'plot', 'pos':('bottom', 'Rm Plot'), 'size':(200, 600), 'labels':{'left':(None,'A'), 'bottom':(None, 's')}}), ('Traces Plot', {'type': 'plot', 'pos':('right', 'ctrl'), 'size':(200, 600), 'labels':{'left':(None,'A'), 'bottom':(None,'s')}}), ]) self.initializeElements() for el in self.getAllElements(): self.getElement(el, create=True) self.tracesPlot = self.getElement('Traces Plot') self.measurementArray = np.zeros(1000, dtype=[ ('unixtime', float), ('time', float), ('Rs', float), ('Rm', float), ('Ih', float) ]) self.files = {} ## keys are dhs, values are {'data': array of time/Rs/Rm/Ih, 'ctrlState': state, 'traces': clampData} self.ctrl.processBtn.clicked.connect(self.processClicked) self.ctrl.saveBtn.clicked.connect(self.saveClicked) def loadFileRequested(self, dhList): """Called by file loader when a file load is requested.""" ## return True if file loads successfully, else return False try: for dh in dhList: #if dh.name is "Patch": #pass if dh is None: continue if dh.isDir(): self.files[dh] = {} #self.files[dh]['traces']=[] #traces = [] self.tracesPlot.clear() i = 0 limitTraces=False if len(dh.subDirs()) > 80: limitTraces=True for f in dh.subDirs(): if i==0 or i%20==0 or not limitTraces: fh = self.dataModel.getClampFile(dh[f]) if fh is not None: self.loadClampData(fh, dh, plot=True) else: break ## assume that once we get one empty protocolDir, all the following ones will be empty too i+=1 return True except: raise def processClicked(self): ## read all the traces from the selected file dh = self.getElement("File Loader").selectedFile() if dh.isDir(): traces = [] for f in dh.subDirs(): fh = self.dataModel.getClampFile(dh[f]) if fh is not None: trace = self.loadClampData(fh, dh, plot=False) traces.append(trace) ## hand list of traces to self.process sequence self.processSequence(traces, dh) def loadClampData(self, f, dh, plot=True): try: data = f.read() except: print f raise #print f.info() time = f.info()['__timestamp__'] #self.files[dh]['traces'].append((data, time)) if plot: self.tracesPlot.plot(data['Channel':'primary']) return (data, time) def processSequence(self, traces, dh=None): if dh is None: dh = self.getElement("File Loader").selectedFile() #self.files[dh]['data'] = np.zeros(len(self.files[dh]['traces']), dtype=self.measurementArray.dtype) self.files[dh]['data'] = np.zeros(len(traces), dtype=self.measurementArray.dtype) #for i, (data, time) in enumerate(self.files[dh]['traces']): for i, (data, time) in enumerate(traces): stats = self.measureParams(data) stats['unixtime'] = time self.files[dh]['data'][i] = stats self.updatePlots() def updatePlots(self): i = len(self.measurementArray) dtype = self.measurementArray.dtype self.measurementArray = np.zeros(i, dtype=dtype) count = 0 for dh in self.files: data = self.files[dh].get('data', np.zeros(0, dtype=dtype)) if len(data) > len(self.measurementArray)-count: self.extendMeasurementArray() self.measurementArray[count:count+len(data)] = data count += len(data) self.measurementArray.sort(order='unixtime') self.measurementArray['time'] = self.measurementArray['unixtime']-self.measurementArray[self.measurementArray['unixtime'] != 0]['unixtime'].min() arr = self.measurementArray[self.measurementArray['unixtime'] != 0] for x in ['Rs', 'Rm', 'Ih']: p = self.getElement(x+' Plot') p.clear() p.plot(arr['time'], arr[x]) self.currentData = arr def extendMeasurementArray(self): i = len(self.measurementArray) arr = np.zeros(i+1000, dtype=self.measurementArray.dtype) arr[0:i] = self.measurementArray self.measurementArray = arr def saveClicked(self): self.saveMA() def saveMA(self, fileName=None): if fileName is None: dh = self.getElement("File Loader").baseDir().name() self.fileDialog = FileDialog(None, "Save traces", dh, '*.ma') self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.fileDialog.show() self.fileDialog.fileSelected.connect(self.saveMA) return #arr = MetaArray(self.currentData) ### need to format this with axes and info arr = MetaArray([self.currentData['Rs'], self.currentData['Rm'], self.currentData['Ih']], info=[ {'name':'vals', 'cols':[ {'name':'Rs', 'units':'Ohms'}, {'name':'Rm', 'units':'Ohms'}, {'name':'Ih', 'units':'A'}]}, {'name':'time', 'units':'s', 'values':self.currentData['time']}]) arr.write(fileName) def measureParams(self, data, display=None): cmd = data['command']['Time':self.ctrl.startSpin.value():self.ctrl.stopSpin.value()] #data = waveform['primary']['Time':self.ctrls['start'].value():self.ctrls['stop'].value()] #print np.argwhere(cmd != cmd[0]) pulseStart = cmd.axisValues('Time')[np.argwhere(cmd != cmd[0])[0][0]] pulseStop = cmd.axisValues('Time')[np.argwhere(cmd != cmd[0])[-1][0]] #print "\n\nAnalysis parameters:", params ## Extract specific time segments nudge = 0.1e-3 base = data['Time': :(pulseStart-nudge)] pulse = data['Time': (pulseStart+nudge):(pulseStop-nudge)] pulseEnd = data['Time': pulseStart+((pulseStop-pulseStart)*2./3.):pulseStop-nudge] end = data['Time': (pulseStop+nudge): ] #print "time ranges:", pulse.xvals('Time').min(),pulse.xvals('Time').max(),end.xvals('Time').min(),end.xvals('Time').max() pulseAmp = pulse['command'].mean() - base['command'].mean() method = str(self.ctrl.methodCombo.currentText()) #print method if method == "Simple Ohm's law": print 'using simple method' if pulseAmp < 0: RsPeak = data['primary'].min() else: RsPeak = data['primary'].max() aRes = pulseAmp/(RsPeak-base['primary'].mean()) iRes = pulseAmp/(pulseEnd['primary'].mean() - base['primary'].mean()) rmc = base['primary'].mean() elif method in ['Santos-Sacchi raw', 'Santos-Sacchi fit']: ### Exponential fit ## v[0] is offset to start of exp ## v[1] is amplitude of exp ## v[2] is tau def expFn(v, t): return (v[0]-v[1]) + v[1] * np.exp(-t / v[2]) ## predictions ar = 10e6 ir = 200e6 #if self.ctrls['mode'].currentText() == 'VC': if True: ## Always want it to use VC settings for now ari = pulseAmp / ar iri = pulseAmp / ir pred1 = [ari, ari-iri, 1e-3] pred2 = [iri-ari, iri-ari, 1e-3] else: #clamp = self.manager.getDevice(self.clampName) try: bridge = data._info[-1]['ClampState']['ClampParams']['BridgeBalResist'] bridgeOn = data._info[-1]['ClampState']['ClampParams']['BridgeBalEnabled'] #bridge = float(clamp.getParam('BridgeBalResist')) ## pull this from the data instead. #bridgeOn = clamp.getParam('BridgeBalEnable') if not bridgeOn: bridge = 0.0 except: bridge = 0.0 #print "bridge:", bridge arv = pulseAmp * ar - bridge irv = pulseAmp * ir pred1 = [arv, -irv, 10e-3] pred2 = [irv, irv, 50e-3] ## Fit exponential to pulse and post-pulse traces tVals1 = pulse.xvals('Time')-pulse.xvals('Time').min() tVals2 = end.xvals('Time')-end.xvals('Time').min() baseMean = base['primary'].mean() fit1 = scipy.optimize.leastsq( lambda v, t, y: y - expFn(v, t), pred1, args=(tVals1, pulse['primary'] - baseMean), maxfev=200, full_output=1) #fit2 = scipy.optimize.leastsq( #lambda v, t, y: y - expFn(v, t), pred2, #args=(tVals2, end['primary'] - baseMean), #maxfev=200, full_output=1, warning=False) #err = max(abs(fit1[2]['fvec']).sum(), abs(fit2[2]['fvec']).sum()) err = abs(fit1[2]['fvec']).sum() ## Average fit1 with fit2 (needs massaging since fits have different starting points) #print fit1 fit1 = fit1[0] #fit2 = fit2[0] #fitAvg = [ ## Let's just not do this. #0.5 * (fit1[0] - (fit2[0] - (fit1[0] - fit1[1]))), #0.5 * (fit1[1] - fit2[1]), #0.5 * (fit1[2] + fit2[2]) #] fitAvg = fit1 (fitOffset, fitAmp, fitTau) = fit1 #print fit1 fitTrace = np.empty(len(data)) ## Handle analysis differently depending on clamp mode #if self.ctrls['mode'].currentText() == 'VC': if True: ## Always use VC mode for now iBase = base['Channel': 'primary'] iPulse = pulse['Channel': 'primary'] iPulseEnd = pulseEnd['Channel': 'primary'] vBase = base['Channel': 'command'] vPulse = pulse['Channel': 'command'] vStep = vPulse.mean() - vBase.mean() sign = [-1, 1][vStep > 0] iStep = sign * max(1e-15, sign * (iPulseEnd.mean() - iBase.mean())) iRes = vStep / iStep #### From Santos-Sacchi 1993: ## 1. compute charge transfered during the charging phase pTimes = pulse.xvals('Time') iCapEnd = pTimes[-1] iCap = iPulse['Time':pTimes[0]:iCapEnd] - iPulseEnd.mean() ## Instead, we will use the fit to guess how much charge transfer there would have been ## if the charging curve had gone all the way back to the beginning of the pulse if method == "Santos-Sacchi fit": iCap = expFn((fit1[1],fit1[1],fit1[2]), np.linspace(0, iCapEnd-pTimes[0], iCap.shape[0])) Q = sum(iCap) * (iCapEnd - pTimes[0]) / iCap.shape[0] Rin = iRes Vc = vStep Rs_denom = (Q * Rin + fitTau * Vc) if Rs_denom != 0.0: Rs = (Rin * fitTau * Vc) / Rs_denom Rm = Rin - Rs Cm = (Rin**2 * Q) / (Rm**2 * Vc) else: Rs = 0 Rm = 0 Cm = 0 aRes = Rs cap = Cm #if self.ctrls['mode'].currentText() == 'IC': if False: ## ic measurements not yet supported in ui iBase = base['Channel': 'command'] iPulse = pulse['Channel': 'command'] vBase = base['Channel': 'primary'] vPulse = pulse['Channel': 'primary'] vPulseEnd = pulseEnd['Channel': 'primary'] iStep = iPulse.mean() - iBase.mean() if iStep >= 0: vStep = max(1e-5, -fitAmp) else: vStep = min(-1e-5, -fitAmp) if iStep == 0: iStep = 1e-14 iRes = (vStep / iStep) aRes = (fitOffset / iStep) + bridge cap = fitTau / iRes rmp = vBase.mean() rmps = vBase.std() rmc = iBase.mean() rmcs = iBase.std() ##print rmp, rmc ## use ui to determine which stats to return stats = np.zeros((1,), dtype=self.measurementArray.dtype) if self.ctrl.RsCheck.isChecked(): stats['Rs'] = aRes if self.ctrl.RmCheck.isChecked(): stats['Rm'] = iRes #if self.ctrls['Capacitance'].isChecked(): #stats['Capacitance'] = cap if self.ctrl.IhCheck.isChecked(): stats['Ih'] = rmc #if self.ctrls['FitError'].isChecked(): #stats['FitError'] = err #if self.ctrls['RestingPotential'].isChecked(): #stats['RestingPotential'] = rmp return stats
class CellHealthTracker(AnalysisModule): def __init__(self, host): AnalysisModule.__init__(self, host) self.ctrlWidget = Qt.QWidget() self.ctrl = CellHealthCtrlTemplate.Ui_widget() self.ctrl.setupUi(self.ctrlWidget) self.ctrlStateGroup = pg.WidgetGroup(self.ctrlWidget) self.ctrl.startSpin.setOpts(step=0.05, suffix='s', siPrefix=True, value=0.35, dec=False) self.ctrl.stopSpin.setOpts(step=0.05, suffix='s', siPrefix=True, value=0.5, dec=False) ## Setup basic GUI self._elements_ = OrderedDict([ ('File Loader', {'type': 'fileInput', 'size': (100, 300), 'host': self, 'args': {'showFileTree': True}}), ('ctrl', {'type': 'ctrl', 'object': self.ctrlWidget, 'pos': ('bottom', 'File Loader'), 'size': (100, 100)}), ('Rs Plot', {'type': 'plot', 'pos':('right', 'File Loader'), 'size':(200, 600), 'labels':{'left':(None,'Ohms'), 'bottom':(None,'s')}}), ('Rm Plot', {'type': 'plot', 'pos':('bottom', 'Rs Plot'), 'size':(200, 600),'labels':{'left':(None,'Ohms'), 'bottom':(None,'s')}}), ('Ih Plot', {'type': 'plot', 'pos':('bottom', 'Rm Plot'), 'size':(200, 600), 'labels':{'left':(None,'A'), 'bottom':(None, 's')}}), ('Traces Plot', {'type': 'plot', 'pos':('right', 'ctrl'), 'size':(200, 600), 'labels':{'left':(None,'A'), 'bottom':(None,'s')}}), ]) self.initializeElements() for el in self.getAllElements(): self.getElement(el, create=True) self.tracesPlot = self.getElement('Traces Plot') self.measurementArray = np.zeros(1000, dtype=[ ('unixtime', float), ('time', float), ('Rs', float), ('Rm', float), ('Ih', float) ]) self.files = {} ## keys are dhs, values are {'data': array of time/Rs/Rm/Ih, 'ctrlState': state, 'traces': clampData} self.ctrl.processBtn.clicked.connect(self.processClicked) self.ctrl.saveBtn.clicked.connect(self.saveClicked) def loadFileRequested(self, dhList): """Called by file loader when a file load is requested.""" ## return True if file loads successfully, else return False try: for dh in dhList: #if dh.name is "Patch": #pass if dh is None: continue if dh.isDir(): self.files[dh] = {} #self.files[dh]['traces']=[] #traces = [] self.tracesPlot.clear() i = 0 limitTraces=False if len(dh.subDirs()) > 80: limitTraces=True for f in dh.subDirs(): if i==0 or i%20==0 or not limitTraces: fh = self.dataModel.getClampFile(dh[f]) if fh is not None: self.loadClampData(fh, dh, plot=True) else: break ## assume that once we get one empty protocolDir, all the following ones will be empty too i+=1 return True except: raise def processClicked(self): ## read all the traces from the selected file dh = self.getElement("File Loader").selectedFile() if dh.isDir(): traces = [] for f in dh.subDirs(): fh = self.dataModel.getClampFile(dh[f]) if fh is not None: trace = self.loadClampData(fh, dh, plot=False) traces.append(trace) ## hand list of traces to self.process sequence self.processSequence(traces, dh) def loadClampData(self, f, dh, plot=True): try: data = f.read() except: print(f) raise #print f.info() time = f.info()['__timestamp__'] #self.files[dh]['traces'].append((data, time)) if plot: self.tracesPlot.plot(data['Channel':'primary']) return (data, time) def processSequence(self, traces, dh=None): if dh is None: dh = self.getElement("File Loader").selectedFile() #self.files[dh]['data'] = np.zeros(len(self.files[dh]['traces']), dtype=self.measurementArray.dtype) self.files[dh]['data'] = np.zeros(len(traces), dtype=self.measurementArray.dtype) #for i, (data, time) in enumerate(self.files[dh]['traces']): for i, (data, time) in enumerate(traces): stats = self.measureParams(data) stats['unixtime'] = time self.files[dh]['data'][i] = stats self.updatePlots() def updatePlots(self): i = len(self.measurementArray) dtype = self.measurementArray.dtype self.measurementArray = np.zeros(i, dtype=dtype) count = 0 for dh in self.files: data = self.files[dh].get('data', np.zeros(0, dtype=dtype)) if len(data) > len(self.measurementArray)-count: self.extendMeasurementArray() self.measurementArray[count:count+len(data)] = data count += len(data) self.measurementArray.sort(order='unixtime') self.measurementArray['time'] = self.measurementArray['unixtime']-self.measurementArray[self.measurementArray['unixtime'] != 0]['unixtime'].min() arr = self.measurementArray[self.measurementArray['unixtime'] != 0] for x in ['Rs', 'Rm', 'Ih']: p = self.getElement(x+' Plot') p.clear() p.plot(arr['time'], arr[x]) self.currentData = arr def extendMeasurementArray(self): i = len(self.measurementArray) arr = np.zeros(i+1000, dtype=self.measurementArray.dtype) arr[0:i] = self.measurementArray self.measurementArray = arr def saveClicked(self): self.saveMA() def saveMA(self, fileName=None): if fileName is None: dh = self.getElement("File Loader").baseDir().name() self.fileDialog = FileDialog(None, "Save traces", dh, '*.ma') self.fileDialog.setAcceptMode(Qt.QFileDialog.AcceptSave) self.fileDialog.show() self.fileDialog.fileSelected.connect(self.saveMA) return #arr = MetaArray(self.currentData) ### need to format this with axes and info arr = MetaArray([self.currentData['Rs'], self.currentData['Rm'], self.currentData['Ih']], info=[ {'name':'vals', 'cols':[ {'name':'Rs', 'units':'Ohms'}, {'name':'Rm', 'units':'Ohms'}, {'name':'Ih', 'units':'A'}]}, {'name':'time', 'units':'s', 'values':self.currentData['time']}]) arr.write(fileName) def measureParams(self, data, display=None): cmd = data['command']['Time':self.ctrl.startSpin.value():self.ctrl.stopSpin.value()] #data = waveform['primary']['Time':self.ctrls['start'].value():self.ctrls['stop'].value()] #print np.argwhere(cmd != cmd[0]) pulseStart = cmd.axisValues('Time')[np.argwhere(cmd != cmd[0])[0][0]] pulseStop = cmd.axisValues('Time')[np.argwhere(cmd != cmd[0])[-1][0]] #print "\n\nAnalysis parameters:", params ## Extract specific time segments nudge = 0.1e-3 base = data['Time': :(pulseStart-nudge)] pulse = data['Time': (pulseStart+nudge):(pulseStop-nudge)] pulseEnd = data['Time': pulseStart+((pulseStop-pulseStart)*2./3.):pulseStop-nudge] end = data['Time': (pulseStop+nudge): ] #print "time ranges:", pulse.xvals('Time').min(),pulse.xvals('Time').max(),end.xvals('Time').min(),end.xvals('Time').max() pulseAmp = pulse['command'].mean() - base['command'].mean() method = str(self.ctrl.methodCombo.currentText()) #print method if method == "Simple Ohm's law": print('using simple method') if pulseAmp < 0: RsPeak = data['primary'].min() else: RsPeak = data['primary'].max() aRes = pulseAmp/(RsPeak-base['primary'].mean()) iRes = pulseAmp/(pulseEnd['primary'].mean() - base['primary'].mean()) rmc = base['primary'].mean() elif method in ['Santos-Sacchi raw', 'Santos-Sacchi fit']: ### Exponential fit ## v[0] is offset to start of exp ## v[1] is amplitude of exp ## v[2] is tau def expFn(v, t): return (v[0]-v[1]) + v[1] * np.exp(-t / v[2]) ## predictions ar = 10e6 ir = 200e6 #if self.ctrls['mode'].currentText() == 'VC': if True: ## Always want it to use VC settings for now ari = pulseAmp / ar iri = pulseAmp / ir pred1 = [ari, ari-iri, 1e-3] pred2 = [iri-ari, iri-ari, 1e-3] else: #clamp = self.manager.getDevice(self.clampName) try: bridge = data._info[-1]['ClampState']['ClampParams']['BridgeBalResist'] bridgeOn = data._info[-1]['ClampState']['ClampParams']['BridgeBalEnabled'] #bridge = float(clamp.getParam('BridgeBalResist')) ## pull this from the data instead. #bridgeOn = clamp.getParam('BridgeBalEnable') if not bridgeOn: bridge = 0.0 except: bridge = 0.0 #print "bridge:", bridge arv = pulseAmp * ar - bridge irv = pulseAmp * ir pred1 = [arv, -irv, 10e-3] pred2 = [irv, irv, 50e-3] ## Fit exponential to pulse and post-pulse traces tVals1 = pulse.xvals('Time')-pulse.xvals('Time').min() tVals2 = end.xvals('Time')-end.xvals('Time').min() baseMean = base['primary'].mean() fit1 = scipy.optimize.leastsq( lambda v, t, y: y - expFn(v, t), pred1, args=(tVals1, pulse['primary'] - baseMean), maxfev=200, full_output=1) #fit2 = scipy.optimize.leastsq( #lambda v, t, y: y - expFn(v, t), pred2, #args=(tVals2, end['primary'] - baseMean), #maxfev=200, full_output=1, warning=False) #err = max(abs(fit1[2]['fvec']).sum(), abs(fit2[2]['fvec']).sum()) err = abs(fit1[2]['fvec']).sum() ## Average fit1 with fit2 (needs massaging since fits have different starting points) #print fit1 fit1 = fit1[0] #fit2 = fit2[0] #fitAvg = [ ## Let's just not do this. #0.5 * (fit1[0] - (fit2[0] - (fit1[0] - fit1[1]))), #0.5 * (fit1[1] - fit2[1]), #0.5 * (fit1[2] + fit2[2]) #] fitAvg = fit1 (fitOffset, fitAmp, fitTau) = fit1 #print fit1 fitTrace = np.empty(len(data)) ## Handle analysis differently depending on clamp mode #if self.ctrls['mode'].currentText() == 'VC': if True: ## Always use VC mode for now iBase = base['Channel': 'primary'] iPulse = pulse['Channel': 'primary'] iPulseEnd = pulseEnd['Channel': 'primary'] vBase = base['Channel': 'command'] vPulse = pulse['Channel': 'command'] vStep = vPulse.mean() - vBase.mean() sign = [-1, 1][vStep > 0] iStep = sign * max(1e-15, sign * (iPulseEnd.mean() - iBase.mean())) iRes = vStep / iStep #### From Santos-Sacchi 1993: ## 1. compute charge transfered during the charging phase pTimes = pulse.xvals('Time') iCapEnd = pTimes[-1] iCap = iPulse['Time':pTimes[0]:iCapEnd] - iPulseEnd.mean() ## Instead, we will use the fit to guess how much charge transfer there would have been ## if the charging curve had gone all the way back to the beginning of the pulse if method == "Santos-Sacchi fit": iCap = expFn((fit1[1],fit1[1],fit1[2]), np.linspace(0, iCapEnd-pTimes[0], iCap.shape[0])) Q = sum(iCap) * (iCapEnd - pTimes[0]) / iCap.shape[0] Rin = iRes Vc = vStep Rs_denom = (Q * Rin + fitTau * Vc) if Rs_denom != 0.0: Rs = (Rin * fitTau * Vc) / Rs_denom Rm = Rin - Rs Cm = (Rin**2 * Q) / (Rm**2 * Vc) else: Rs = 0 Rm = 0 Cm = 0 aRes = Rs cap = Cm #if self.ctrls['mode'].currentText() == 'IC': if False: ## ic measurements not yet supported in ui iBase = base['Channel': 'command'] iPulse = pulse['Channel': 'command'] vBase = base['Channel': 'primary'] vPulse = pulse['Channel': 'primary'] vPulseEnd = pulseEnd['Channel': 'primary'] iStep = iPulse.mean() - iBase.mean() if iStep >= 0: vStep = max(1e-5, -fitAmp) else: vStep = min(-1e-5, -fitAmp) if iStep == 0: iStep = 1e-14 iRes = (vStep / iStep) aRes = (fitOffset / iStep) + bridge cap = fitTau / iRes rmp = vBase.mean() rmps = vBase.std() rmc = iBase.mean() rmcs = iBase.std() ##print rmp, rmc ## use ui to determine which stats to return stats = np.zeros((1,), dtype=self.measurementArray.dtype) if self.ctrl.RsCheck.isChecked(): stats['Rs'] = aRes if self.ctrl.RmCheck.isChecked(): stats['Rm'] = iRes #if self.ctrls['Capacitance'].isChecked(): #stats['Capacitance'] = cap if self.ctrl.IhCheck.isChecked(): stats['Ih'] = rmc #if self.ctrls['FitError'].isChecked(): #stats['FitError'] = err #if self.ctrls['RestingPotential'].isChecked(): #stats['RestingPotential'] = rmp return stats