def _scanFC(dev, center, step, numpoints, sname, *args, **kwargs): """ The scan is performed around *center* with the given parameters. This supports all arguments and keyword arguments that `cscan()` supports, and additionally: * a keyword "fit", that specifies the fit function to use. Defaults to gaussian if not specified. Use `ListFitters()` to see possible values. * a keyword "ycol" that gives the Y column of the dataset to use for the fit. """ ycol = kwargs.pop('ycol', -1) fitname = kwargs.pop('fit', 'gauss') fitclass = FitterRegistry.getFitterCls(fitname) if fitclass.center_index is None: raise UsageError('Fit class is not suitable for centering.') cscan(dev, center, step, numpoints, sname, *args, **kwargs) params, _ = fit(fitclass, ycol) newcenter = params[fitclass.center_index] if params is not None else None minvalue = center - step * numpoints maxvalue = center + step * numpoints return minvalue, newcenter, maxvalue
def checkalign(hkl, step, numpoints, *args, **kwargs): """Readjust sample psi0 by scanning a peak. The psi0 is adjusted to the fitted center of a sample rocking scan around the (h, k, l) position. After the scan, moves back to (h, k, l). An additional a keyword "ycol" gives the Y column of the dataset to use for the Gaussian fit (see the help for `gauss()` for the meaning of this parameter). Examples: >>> checkalign((1, 1, 0), 0.05, 20) >>> checkalign((1, 1, 0), 0.05, 20, ycol='ctr2') You can also give a keyword argument "accuracy", which defines how much the old and new value have to differ in order to make an adjustment. It defaults to 1/10 of the fitted FWHM. """ tas = session.instrument ycol = kwargs.pop('ycol', -1) accuracy = kwargs.pop('accuracy', None) target = tuple(hkl) + (0, ) tas.maw(target) psi = tas._attached_psi center = tas._calpos(target + (None, None), printout=False)[3] cscan(psi, center, step, numpoints, 'align check', *args, **kwargs) params, _ = gauss(ycol) # do not allow moving outside of the scanned region minvalue = center - step * numpoints maxvalue = center + step * numpoints if params is None: session.log.warning('Gaussian fit failed, psi0 unchanged') elif not minvalue <= params[0] <= maxvalue: session.log.warning('Gaussian fit resulted in center outside scanning ' 'area, offset unchanged') else: # NOTE: this is the other way around compared to checkoffset diff = center - params[0] session.log.info('center of Gaussian fit at %s', psi.format(params[0], True)) if accuracy is None: accuracy = min(params[2] * 0.05, 0.1) if abs(diff) < accuracy: session.log.info( 'alignment ok within %.3f degrees, not ' 'changing psi0', accuracy) else: sample = tas._attached_cell session.log.warning('adjusting %s.psi0 by %s', sample, psi.format(diff, True)) sample.psi0 += diff tas.maw(target)
def ScanOmega(hkl, preset=1., subscan=False): instr = session.instrument if not isinstance(instr, SXTalBase): raise NicosError('your instrument device is not a SXTAL device') width = instr.getScanWidthFor(hkl) sps = instr.scansteps sw = width / sps op = instr._attached_omega.read(0) cscan(instr._attached_omega, op, sw, sps // 2, instr, preset, subscan=subscan)
def ScanT2T(hkl, preset=1., subscan=False): instr = session.instrument if not isinstance(instr, SXTalBase): raise NicosError('your instrument device is not a SXTAL device') width = instr.getScanWidthFor(hkl) sps = instr.scansteps sw = width / sps instr.maw(hkl) op = instr._attached_omega.read(0) tp = instr._attached_ttheta.read(0) cscan([instr._attached_omega, instr._attached_ttheta], [op, tp], [sw, 2 * sw], sps // 2, instr, preset, subscan=subscan)
def test_powderfit_from_data(session): tasdev = session.getDevice('Tas') tasdev.scanconstant = 2.0 sample = session.getDevice('Sample') phidev = session.getDevice('t_phi') tasdet = session.getDevice('vtasdet') sample.lattice = [12.38, 12.38, 12.38] sample.orient1 = [1, 0, 0] sample.orient2 = [0, 1, 1] peaks = [(4, 0, 0, 0), (2, 1, 1, 0), (0, 2, 2, 0)] for peak in peaks: tasdev.maw(peak) cscan(phidev, phidev(), 0.2, 10, 1, tasdet) # since datasets are not numbered (no sink), number 0 will catch all res = powderfit('YIG', scans=[0]) assert -0.105 <= res[0] <= 0.105 assert -0.105 <= res[1] <= 0.105
def test_cscan(session): m = session.getDevice('motor') cscan(m, 0, 1, 2) dataman = session.experiment.data dataset = dataman.getLastScans()[-1] assert dataset.devvaluelists == [[-2.], [-1.], [0.], [1.], [2.]]
def centerpeak(*args, **kwargs): """Center a peak with multiple scans over multiple devices. This does repeated scans of all devices to iteratively center a peak until the peak center does not shift anymore. Starting position is the current position of all devices. Non-keyword arguments are devices to scan over. At least one is required. Supported keyword arguments are: * ``rounds`` - maximum number of rounds (a round is one scan per device). The default is 5. * ``steps`` - number of steps per side for each scan. The default is 15. * ``steps_devname`` - special number of steps per side for this device. * ``step`` - step size for each scan. The default is 0.1. * ``step_devname`` - special step size for this device. * ``convergence`` - maximum delta of peak center between two scans of a device, in units of the scan step size. The default is 0.5. * ``fit`` - fit function to use for determining peak center, see below. * ``cont`` - True/False whether to use continuous scans. Default is false. * all further keyword arguments (like ``t=1``) are used as detector presets. Examples:: # default scan without special options, count 2 seconds >>> centerpeak(omega, gamma, 2) # scan with device-specific step size and number of steps >>> centerpeak(omega, gamma, step_omega=0.05, steps_omega=20, t=1) # allow a large number of rounds with very small convergence window # (1/5 of step size) >>> centerpeak(omega, gamma, rounds=10, convergence=0.2, t=1) # center using Gaussian peak fits >>> centerpeak(omega, gamma, fit='gauss', t=1) Fit functions: * ``'center_of_mass'``: default, works for any peak shape * ``'gauss'``: symmetric Gaussian """ nrounds = 5 devices = [] defsteps = 15 nsteps = {} defstepsize = 0.1 stepsizes = {} preset = {} fit = 'center_of_mass' allowed_fit = {'center_of_mass', 'gauss'} continuous = False convergence = 0.5 for devname in args: if isinstance(devname, number_types): preset['t'] = devname else: devices.append(session.getDevice(devname, Moveable)) if not devices: raise UsageError('need at least one device to scan over') for kw, value in kwargs.items(): if kw == 'rounds': nrounds = value elif kw == 'fit': if value not in allowed_fit: raise UsageError('fit function %s is not allowed' % value) fit = value elif kw == 'convergence': convergence = value elif kw == 'steps': defsteps = value elif kw == 'step': defstepsize = value elif kw == 'cont': continuous = bool(value) elif kw.startswith('step_'): dev = session.getDevice(kw[5:], Moveable) if dev not in devices: raise UsageError('device %s not in list of devices to scan' % dev) stepsizes[dev] = value elif kw.startswith('steps_'): dev = session.getDevice(kw[6:], Moveable) if dev not in devices: raise UsageError('device %s not in list of devices to scan' % dev) nsteps[dev] = value else: preset[kw] = value for dev in devices: if dev not in stepsizes: stepsizes[dev] = defstepsize if dev not in nsteps: nsteps[dev] = defsteps # main loop lastround = {dev: dev.read() for dev in devices} for i in range(nrounds): session.log.info('Round %d of %d', i + 1, nrounds) session.log.info('*' * 100) # results of last round thisround = {} for dev in devices: center = lastround[dev] if continuous: contscan(dev, center - nsteps[dev] * stepsizes[dev], center + nsteps[dev] * stepsizes[dev], speed=stepsizes[dev] / preset.get('t', 1), timedelta=preset.get('t', 1)) else: cscan(dev, center, stepsizes[dev], nsteps[dev], *devices, **preset) if session.mode == SIMULATION: thisround[dev] = center elif fit == 'center_of_mass': thisround[dev] = center_of_mass() elif fit == 'gauss': params, _ = gauss() minvalue = center - abs(stepsizes[dev] * nsteps[dev]) maxvalue = center + abs(stepsizes[dev] * nsteps[dev]) if params is None: maw(dev, center) session.log.error('no Gaussian fit found in this scan') return fit_center, fit_ampl, _fit_fwhm, fit_bkgd = params if math.isnan(fit_center) or \ not (minvalue <= fit_center <= maxvalue) or \ fit_ampl < 0.5 * fit_bkgd: maw(dev, center) session.log.error('Gaussian peak too small, or center ' 'outside scanning area') return thisround[dev] = fit_center maw(dev, thisround[dev]) session.log.info('*' * 100) again = False for dev in devices: diff = abs(lastround[dev] - thisround[dev]) session.log.info('%-10s center: %8.6g -> %8.6g (delta %8.6g)', dev, lastround[dev], thisround[dev], diff) if session.mode == SIMULATION: again = i < 1 if i == 1: session.log.info('=> dry run: limiting to 2 rounds') elif diff > convergence * stepsizes[dev]: if i == nrounds - 1: session.log.info( '=> would need another round, but command' ' limited to %d rounds', nrounds) else: session.log.info('=> needs another round') again = True if not again: session.log.info('=> found convergence on peak:') for dev in devices: session.log.info('%-10s : %s', dev, dev.format(dev())) return lastround = thisround
def ScanDataset(name, speed=None, timedelta=None, start=1, cont=True): """Do an omega-scan over all HKL reflections in a given CSV dataset. Takes a CSV file created by `GenDataset()` (maybe edited) and does an omega scan over all HKLs in the list. Use ``start=N`` to start at a different line than the first. Use ``cont=False`` to select step scans instead of continuous scans. Examples:: # omega scan of all peaks in "low_t" with default settings >>> ScanDataset('low_t') # same, but with speed 0.1 and timedelta 1 second >>> ScanDataset('low_t', 0.1, 1) # start at row 100 >>> ScanDataset('low_t', start=100) # with stepwise scans >>> ScanDataset('low_t', 0.1, 1, cont=False) """ instr = session.instrument root = session.experiment.samplepath fullpath = path.join(root, name + '.csv') session.log.info('Reading reflection list from %s.', fullpath) all_pos = [] with open(fullpath, 'r', encoding='utf-8') as fp: reader = csv.reader(fp) for row in reader: if row[0] == 'h': continue h, k, l, _, _, _, width = row[:7] all_pos.append(([float(h), float(k), float(l)], float(width.replace(',', '.')))) session.log.info('%d reflections read.', len(all_pos)) if start != 1: if len(all_pos) < start: session.log.info('Nothing to do.') return else: session.log.info('Starting at reflection number %d.', start) all_pos = all_pos[start - 1:] Remark('Scan dataset %s (%d reflections)' % (name, len(all_pos))) skipped = 0 for i, (hkl, width) in enumerate(all_pos, start=start): session.log.info('') info = (i, len(all_pos) + start - 1, hkl[0], hkl[1], hkl[2]) text = '*** Scanning %d/%d: (%4.4g %4.4g %4.4g)' % info scope = 'HKL %d/%d: (%4.4g %4.4g %4.4g)' % info if skipped: text += ' [%d skipped]' % skipped scope += ' [%d skipped]' % skipped session.log.info(text) session.beginActionScope(scope) try: calc = dict(instr._extractPos(instr._calcPos(hkl))) om1 = calc['omega'] - width / 2. om2 = calc['omega'] + width / 2. # optimization to avoid unnecessary omega movement, # currently disabled due to backlash concerns # # cur_om = instr._attached_omega.read() # if abs(cur_om - om1) > abs(cur_om - om2): # om1, om2 = om2, om1 umin, umax = instr._attached_omega.userlimits if om1 < umin: om1 = umin if om1 > umax: om1 = umax try: maw(instr._attached_gamma, calc['gamma'], instr._attached_nu, calc['nu'], instr._attached_omega, om1) except SKIP_EXCEPTIONS: session.log.warning('Skipping scan', exc=1) skipped += 1 continue except CONTINUE_EXCEPTIONS: session.log.warning('Positioning problem, continuing', exc=1) if cont: contscan(instr._attached_omega, om1, om2, speed, timedelta, '(%4.4g %4.4g %4.4g)' % info[2:]) else: stepsize = speed * timedelta # steps per side, not including the central point nsteps = int(round(width / 2 / stepsize)) cscan(instr._attached_omega, calc['omega'], stepsize, nsteps, t=timedelta) finally: session.endActionScope()