def Q(*args, **kwds): # pylint: disable=E0102 """A Q-E vector object that can be used for calculations. Use as follows: To create a Q vector (1, 0, 0) with energy transfer 0 or 5: >>> q = Q(1) >>> q = Q(1, 0, 0) >>> q = Q(1, 0, 0, 5) >>> q = Q(h=1, E=5) To create a Q vector from another Q vector, adjusting one or more entries: >>> q2 = Q(q, h=2, k=1) >>> q2 = Q(q, E=0) You can then use the Q-E vectors in scanning commands: >>> qscan(q, q2, 5, t=10) """ q = _Q(4) q[:] = 0. if not args and not kwds: return q if len(args) == 1: try: nlen = len(args[0]) except TypeError: q[0] = args[0] else: for i in range(nlen): q[i] = args[0][i] elif len(args) > 4: raise UsageError('1 to 4 Q/E components are allowed') else: for i in range(len(args)): q[i] = args[i] if 'h' in kwds: q[0] = kwds['h'] elif 'H' in kwds: q[0] = kwds['H'] if 'k' in kwds: q[1] = kwds['k'] elif 'K' in kwds: q[1] = kwds['K'] if 'l' in kwds: q[2] = kwds['l'] if 'L' in kwds: q[2] = kwds['L'] if 'E' in kwds: q[3] = kwds['E'] elif 'e' in kwds: q[3] = kwds['e'] return q
def findpeaks(*columns, **kwds): """Find peaks the data of the last scan. Peaks are determined by the property that a data point is bigger than some neighbors (by default 2) on every side. Then a parabola is fitted to these points, which gives a good approximation to the peak center position. The return value is a list of possible X values of peaks. The following keyword arguments are accepted: * *npoints* -- number of neighbors (default 2) per side of peak to be considered: if the scan is very fine, this must be increased """ # parabola model def model(x, b, s, c): return b + s * (x - c)**2 xs, ys, dys = _getData(columns)[:3] np = kwds.get('npoints', 2) peaks = [] # peak has to be different from background minpeakheight = (ys.max() - ys.min()) * 0.1 + ys.min() for i in range(len(xs) - 2 * np): subys = ys[i:i + 2 * np + 1] subdys = dys[i:i + 2 * np + 1] # need a peak of at least minpeakheight if subys.max() < minpeakheight: continue # peak must be bigger than sides - including errors! miny = subys[np] - subdys[np] if (subys[0] + subdys[0] > miny) or \ (subys[2*np] + subdys[2*np] > miny): continue # values must rise to peak and fall down for v in range(np): if subys[v] > subys[v + 1] or subys[v + np] < subys[v + np + 1]: break else: # we (probably) have a peak subxs = xs[i:i + 2 * np + 1] b = subys[np] s = (subys[0] - subys[np]) / (subxs[0] - subxs[np])**2 c = subxs[np] fit = Fit('parabola', model, ['b', 's', 'c'], [b, s, c]) res = fit.run(subxs, subys, subdys) if not res._failed: peaks.append(res._pars[1][2]) return peaks
def doInit(self, mode): phases = [0, 0] self._dev = self._attached_io try: if mode == SIMULATION: raise NicosError('not possible in dry-run/simulation mode') wavelength = self._read(WAVE_LENGTH) / 1000.0 if wavelength == 0.0: wavelength = 4.5 self._setROParam('wavelength', wavelength) self._setROParam('speed', round(self._read(SPEED) / 1118.4735)) self._setROParam('ratio', abs(self._read(RATIO))) slittype = self._read(SLIT_TYPE) if slittype == 2: # XXX this looks strange self._setROParam('slittype', 1) else: self._setROParam('slittype', 0) crc = self._read(CRM) if crc == 1: self._setROParam('crc', 0) else: self._setROParam('crc', 1) for ch in range(2, 8): phases.append( int(round(self._read(PHASE_SET + ch * 100) / 466.0378))) self._setROParam('phases', phases) except NicosError: self._setROParam('wavelength', 4.5) self._setROParam('speed', 0) self._setROParam('ratio', 1) self._setROParam('slittype', 0) self._setROParam('crc', 1) self._setROParam('phases', [0] * 8) self.log.warning('could not read initial data from PMAC chopper ' 'controller', exc=1)
def stepHistoryUntil(self, prefix, direction): if direction == 'up': lookrange = range(self._current - 1, -1, -1) else: lookrange = range(self._current + 1, len(self.history)) for i in lookrange: if self.history[i].startswith(prefix): self._current = i self.setText(self.history[i]) self.setCursorPosition(len(prefix)) return if direction == 'down': # nothing found: go back to start self._current = -1 self.setText(self._start_text) self.setCursorPosition(len(prefix))
def twodscan(dev1, start1, step1, numpoints1, dev2, start2, step2, numpoints2, *args, **kwargs): """Two-dimensional scan of two devices. This is a convenience function that runs a number of scans over *dev2* with *dev1* at a different position for each. Example: >>> twodscan(phi, 0, 1, 10, psi, 0, 2, 10, t=1) """ for j in range(numpoints1): dev1value = start1 + j * step1 try: dev1.maw(dev1value) except NicosError as err: if isinstance(err, CONTINUE_EXCEPTIONS): session.log.warning( 'Positioning problem of %s at %s, ' 'scanning %s anyway', dev1, dev1.format(dev1value, unit=True), dev2, exc=1) elif isinstance(err, SKIP_EXCEPTIONS): session.log.warning('Skipping scan at %s = %s', dev1, dev1.format(dev1value, unit=True), exc=1) continue else: raise scan(dev2, start2, step2, numpoints2, dev1, *args, **kwargs)
def setCommand(self, command): if self.getCommand() == command: return self.moveCursor(QTextCursor.End) self.moveCursor(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) for _ in range(len(self.ps1)): self.moveCursor(QTextCursor.Right, QTextCursor.KeepAnchor) self.textCursor().removeSelectedText() self.textCursor().insertText(command) self.moveCursor(QTextCursor.End)
def adjust(self, angle=0.0): while angle > 180.0: angle -= 360 while angle < -180.0: angle += 360 self._setROParam('phases', [0, -26, 1, 19, 13, 1, -13, 62]) self._setROParam('speed', 0) for ch in range(1, 8): self._writevalues(ch, 0, self.speed, self.phases[ch] + angle) self._setROParam('changetime', currenttime())
def clearAlmostEverything(self): # Clears all messages, except the last input command with its output. # This is used for clearing output on NewExperiment, because the event # that clears the messages arrives *after* the command has run. msgs = self._messages[:] self.clear() i = 0 for i in range(len(msgs) - 1, -1, -1): if msgs[i][2] == INPUT: break self.addMessages(msgs[i:])
def adjust(self, angle=0.0): while angle > 180.0: angle -= 360 while angle < -180.0: angle += 360 self._setROParam('phases', [0, -26, 1, 19, 13, 1, -13, 62]) self._setROParam('speed', 0) for ch in range(1, 8): self._attached_discs[ch].move(self.speed) self._attached_discs[ch].phase = self._attached_discs[ch] + angle self._setROParam('changetime', currenttime())
def getLatest(self, n=5): # Return latest n commands together with warning/error output. inputcount = 0 retmsgs = [] for i in range(len(self._messages) - 1, -1, -1): if self._messages[i][2] == INPUT: retmsgs.append(self._messages[i]) inputcount += 1 if inputcount == n: break elif self._messages[i][2] >= WARNING: retmsgs.append(self._messages[i]) return retmsgs[::-1]
def _change(self, name, value): """Internal interface to change a chopper value.""" if not self._is_cal(): raise NicosError(self, 'chopper system not yet calibrated') if name == 'wavelength': self._setROParam('wavelength', round(value * 1000.0) / 1000.0) elif name == 'speed': assert 150 <= value <= 22000 self._setROParam('speed', value) elif name == 'ratio': assert value in range(1, 11) self._setROParam('ratio', value) elif name == 'crc': assert value in [0, 1] self._setROParam('crc', value) elif name == 'slittype': assert value in [0, 1, 2] self._setROParam('slittype', value) # calculate new phases phases = [0] for ch in range(1, 8): phi = calc.phi(ch, self.speed, self.wavelength, self.crc, self.slittype, self.ratio, self.ch5_90deg_offset) assert -180. <= phi <= 180. phases.append(int(round(100.0 * phi))) r1, r2 = (2, -1.0) if self.crc == 0 else (1, 1.0) rr = self.ratio + 1 self._writevalues(1, 0, self.speed, phases[1], int(round(self.wavelength * 1000.0))) self._writevalues(2, r1, r2 * self.speed, phases[2], int(self.slittype + 1)) self._writevalues(3, 1, self.speed, phases[3], int(self.crc + 1)) self._writevalues(4, 1, self.speed, phases[4]) self._writevalues(5, rr, -1 * self.speed, phases[5]) self._writevalues(6, 1, self.speed, phases[6]) self._writevalues(7, r1, r2 * self.speed, phases[7]) self._setROParam('phases', phases) self._setROParam('changetime', currenttime())
def qscan(Q, dQ, numpoints, *args, **kwargs): """Perform a single-sided Q scan. The *Q* and *dQ* arguments can be lists of 3 or 4 components, or a `Q` object. Example: >>> qscan((1, 0, 0, 0), (0, 0, 0, 0.1), 11, kf=1.55, mon1=100000) will perform an energy scan at (100) from 0 to 1 meV (or THz, depending on the instrument setting) with the given constant kf and the given monitor counts per point. The special "plot" parameter can be given to plot the scan instead of running it: * plot='res' -- plot resolution ellipsoid along scan * plot='hkl' -- plot position of scan points in scattering plane """ Q, dQ = _getQ(Q, 'Q'), _getQ(dQ, 'dQ') scanstr = _infostr('qscan', (Q, dQ, numpoints) + args, kwargs) plotval = kwargs.pop('plot', None) preset, scaninfo, detlist, envlist, move, multistep, Q, dQ = \ _handleQScanArgs(args, kwargs, Q, dQ, scanstr) if all(v == 0 for v in dQ) and numpoints > 1: raise UsageError('scanning with zero step width') values = [[(Q[0] + i * dQ[0], Q[1] + i * dQ[1], Q[2] + i * dQ[2], Q[3] + i * dQ[3])] for i in range(numpoints)] if plotval == 'res': resscan(*(p[0] for p in values), kf=kwargs.get('kf'), ki=kwargs.get('ki')) elif plotval == 'hkl': hklplot(scan=[p[0] for p in values], kf=kwargs.get('kf'), ki=kwargs.get('ki')) else: scan = QScan(values, move, multistep, detlist, envlist, preset, scaninfo) scan.run()
def qcscan(Q, dQ, numperside, *args, **kwargs): """Perform a centered Q scan. The *Q* and *dQ* arguments can be lists of 3 or 4 components, or a `Q` object. Example: >>> qcscan((1, 0, 0, 1), (0.001, 0, 0, 0), 20, mon1=1000) will perform a longitudinal scan around (100) with the given monitor counts per point. The special "plot" parameter can be given to plot the scan instead of running it: * plot='res' -- plot resolution ellipsoid along scan * plot='hkl' -- plot position of scan points in scattering plane """ Q, dQ = _getQ(Q, 'Q'), _getQ(dQ, 'dQ') scanstr = _infostr('qcscan', (Q, dQ, numperside) + args, kwargs) plotval = kwargs.pop('plot', None) preset, scaninfo, detlist, envlist, move, multistep, Q, dQ = \ _handleQScanArgs(args, kwargs, Q, dQ, scanstr) if all(v == 0 for v in dQ) and numperside > 0: raise UsageError('scanning with zero step width') values = [[(Q[0] + i * dQ[0], Q[1] + i * dQ[1], Q[2] + i * dQ[2], Q[3] + i * dQ[3])] for i in range(-numperside, numperside + 1)] if plotval == 'res': resscan(*(p[0] for p in values), kf=kwargs.get('kf'), ki=kwargs.get('ki')) elif plotval == 'hkl': hklplot(scan=[p[0] for p in values], kf=kwargs.get('kf'), ki=kwargs.get('ki')) else: scan = QScan(values, move, multistep, detlist, envlist, preset, scaninfo) scan.run()
def mkpos(starts, steps, numpoints): return [[start + i * step for (start, step) in zip(starts, steps)] for i in range(numpoints)]
def setCursorPosition(self, position): self.moveCursor(QTextCursor.StartOfLine) for _ in range(len(self.ps1) + position): self.moveCursor(QTextCursor.Right)
def mkpos(centers, steps, numperside): return [[ center + (i - numperside) * step for (center, step) in zip(centers, steps) ] for i in range(2 * numperside + 1)]
def appendscan(numpoints=5, stepsize=None): """Go on *numpoints* steps from the end of the last scan. *numpoints* can also be negative to prepend scan points. Examples: >>> appendscan(5) # append 5 more points to last scan >>> appendscan(-5) # append 5 more points to beginning of last scan If *stepsize* is given, the step size of the last scan will be overridden. Example: >>> scan(x, 10, 0.1, 10) # original scan >>> appendscan(10, 0.5) # continue the scan, but with other step size If the previous scan wasn't a scan with fixed step size and *stepsize* is not given, the new step size will be calculated as the averaged step size from the previous scan: >>> scan(x, [0, 0.1, 0.2, 0.5, 1]) >>> appendscan(5) moves x to the following positions: ==== ==== Step x ==== ==== 1/5 1.25 2/5 1.50 3/5 1.75 4/5 2.00 5/5 2.25 ==== ==== The scan data will be plotted into the same live plot, if possible, but will be saved into a separate data file. Scans with multiple devices are supported: >>> scan([x, y], [0, 1], [0.1, 0.2], 10) >>> appendscan(3, [0.05, 0.1]) # append 5 points with new stepsizes If the original scan was made over multiple devices, the *stepsize* must be a list with the same number of elements. """ if numpoints == 0: raise UsageError('number of points must be either positive or ' 'negative') direction = numpoints / abs(numpoints) dslist = session.experiment.data.getLastScans() if not dslist: raise NicosError('no last scan saved') contuids = [] # Find the last scan that wasn't an appendscan. i = len(dslist) - 1 while i >= 0: contuids.append(dslist[i].uid) if not dslist[i].continuation: break i -= 1 # If the last scan was an appendscan in the same direction, append to it, # else append to the original scan. This DWUMs for # scan(...) # appendscan(5) # appendscan(2) # appendscan(-3) if dslist[-1].cont_direction == direction: scan = dslist[-1] # to continue an appendscan in the negative direction, which has # the points reversed, we need to reverse the direction again if scan.cont_direction == -1: numpoints = -numpoints else: scan = dslist[i] n_devs = len(scan.devices) n_steps = len(scan.subsets) if n_steps < 2: raise NicosError('cannot append to scan with no positions') if stepsize is not None: if isinstance(stepsize, number_types): stepsize = [stepsize] if n_devs != len(stepsize): raise NicosError('the stepsize must have %d elements' % n_devs) else: stepsize = [None] * n_devs # create the new list of positions positions = [[None] * n_devs for _ in range(abs(numpoints))] # for each device involved in the scan for i in range(n_devs): # determine step size by first and last position pos1 = scan.startpositions[0][i] pos2 = scan.startpositions[n_steps - 1][i] if isinstance(pos1, (tuple, ndarray)): stepsizes = tuple( (b - a) / (n_steps - 1) for (a, b) in zip(pos1, pos2)) if numpoints > 0: for j in range(1, numpoints + 1): positions[j - 1][i] = tuple( b + j * s for (b, s) in zip(pos2, stepsizes)) else: for j in range(1, -numpoints + 1): positions[j - 1][i] = tuple( a - j * s for (a, s) in zip(pos1, stepsizes)) elif isinstance(pos1, number_types): if stepsize[i] is None: stepsize[i] = (pos2 - pos1) / (n_steps - 1) if numpoints > 0: startpos = pos2 + stepsize[i] else: stepsize[i] = -stepsize[i] startpos = pos1 + stepsize[i] for j in range(abs(numpoints)): positions[j][i] = startpos + j * stepsize[i] else: # we can't produce new values for this device raise NicosError('cannot append to this scan; some devices ' 'have nonnumeric values') numpoints = abs(numpoints) s = Scan(scan.devices, positions, [], None, None, scan.detectors, scan.environment, scan.preset, '%d more steps of last scan' % numpoints) s._xindex = scan.xindex s._continuation = contuids s._cont_direction = direction # envlist must be reset since Scan.__init__ messes with the ordering s._envlist[:] = scan.environment s.run()
def doStatus(self, maxage=0): errstates = { # 0: 'inactive', 1: 'cal', 2: 'com', 8: 'estop', } ret = [] stval = status.OK # read status values for ch in range(1, 8): state = self._read(STATUS + ch) if state in errstates: stval = status.ERROR ret.append('ch %d: state is %s' % (ch, errstates[state])) # read speeds for ch in range(1, 8): if (self.crc == 0 and ch in [2, 7]) or ch == 5: r2 = -1.0 else: r2 = 1.0 speed = self._read(ACT_SPEED + ch) * r2 rat = 1.0 if ch == 5: if self.ratio > 1 and self.ratio <= 8: rat = self.ratio / (self.ratio - 1.0) elif self.ratio > 8: rat = self.ratio / 7.0 nominal = self.speed / rat maxdelta = self.speed_accuracy / rat if abs(speed - nominal) > maxdelta: msg = 'ch %d: speed %.2f != nominal %.2f' % (ch, speed, nominal) ret.append(msg) if self.isTimedOut(): stval = status.OK # NOTREACHED self.log.warning(msg) else: stval = status.BUSY # read phases for ch in range(2, 8): phase = self._readphase(ch) phase_diff = abs(phase - self.phases[ch] / 100.) if phase_diff > self.phase_accuracy: if self.isTimedOut(): # Due to some problems with the electronics the phase of # the chopper disc 5 may have a phase differs from real # value in the range of 360 or 270 degrees if ch == 5: msg = 'cd 5 phase %.2f != %.2f' % ( phase, self.phases[ch] / 100.) if phase_diff >= 360: if phase_diff % 360 <= self.phase_accuracy: self.log.warning(msg) continue elif phase_diff > 180: if phase_diff >= (360. - self.phase_accuracy) or \ abs(phase_diff - 270) <= self.phase_accuracy: self.log.warning(msg) continue stval = status.ERROR else: stval = status.ERROR else: stval = status.BUSY msg = 'ch %d: phase %s != nominal %s' % ( ch, phase, self.phases[ch] / 100.) ret.append(msg) if self.isTimedOut(): stval = status.OK # NOTREACHED self.log.warning(msg) else: stval = status.BUSY return stval, ', '.join(ret) or 'normal'
def _readspeeds_actual(self): return [abs(self._read(CURRENT_SPEED + ch * 100)) for ch in range(1, 8)]
def _readspeeds(self): return [abs(self._read(ACT_SPEED + ch)) for ch in range(1, 8)]
def _stop(self): self._phases = [0] + [4500] * 7 self._setROParam('speed', 0) for ch in range(1, 8): self._writevalues(ch, 0, self.speed, self._phases[ch]) self._setROParam('changetime', currenttime())
def _change(self, name, value): """Internal interface to change a chopper value.""" if name == 'wavelength': self._setROParam('wavelength', round(value * 1000.0) / 1000.0) elif name == 'speed': assert 150 <= value <= 22000 self._setROParam('speed', value) elif name == 'ratio': assert value in range(1, 11) self._setROParam('ratio', value) elif name == 'crc': assert value in [0, 1] self._setROParam('crc', value) elif name == 'slittype': assert value in [0, 1, 2] self._setROParam('slittype', value) # calculate new phases phases = [0] for ch in range(1, 8): phi = calc.phi(ch, self.speed, self.wavelength, self.crc, self.slittype, self.ratio, self.ch5_90deg_offset) assert -180. <= phi <= 180. phases.append(phi) r1, r2 = (2, -1.0) if self.crc == 0 else (1, 1.0) self._attached_discs[0].move(self.speed) self._attached_discs[0].phase = phases[1] self._attached_discs[1].move(r2 * self.speed) self._attached_discs[1].phase = phases[2] self._attached_discs[1].gear = r1 self._attached_discs[1].slittype = self.slittype + 1 self._attached_discs[2].move(self.speed) self._attached_discs[2].gear = 1 self._attached_discs[2].phase = phases[3] self._attached_discs[2].crc = self.crc + 1 self._attached_discs[3].move(self.speed) self._attached_discs[3].phase = phases[4] self._attached_discs[3].gear = 1 # XXX if ratio == 1 then speed = 0 ? if self.ratio > 1: if self.ratio < 9: self._attached_discs[4].move(-self.speed * (self.ratio - 1) / self.ratio) else: self._attached_discs[4].move(-self.speed * 7. / self.ratio) else: self._attached_discs[4].move(-self.speed) self._attached_discs[4].phase = phases[5] self._attached_discs[4].gear = self.ratio + 1 self._attached_discs[5].move(self.speed) self._attached_discs[5].phase = phases[6] self._attached_discs[5].gear = 1 self._attached_discs[6].move(r2 * self.speed) self._attached_discs[6].phase = phases[7] self._attached_discs[6].gear = r1 self._setROParam('phases', phases) self._setROParam('changetime', currenttime())
def _is_cal(self): for ch in range(1, 8): ret = self._read(STATUS + ch) if ret in [ST_INACTIVE, ST_CALIB1, ST_CALIB2, ST_IDLE, ST_ESTOP]: return False return True