Source code for APS_BlueSky_tools.examples


"""
Examples that might be useful at the APS using BlueSky

.. autosummary::
   
   ~SynPseudoVoigt

"""

# Copyright (c) 2017-2018, UChicago Argonne, LLC.  See LICENSE file.

import logging
import ophyd.sim
import numpy as np


logger = logging.getLogger(__name__).addHandler(logging.NullHandler())


[docs]class SynPseudoVoigt(ophyd.sim.SynSignal): """ Evaluate a point on a pseudo-Voigt based on the value of a motor. Provides a signal to be measured. Acts like a detector. :see: https://en.wikipedia.org/wiki/Voigt_profile PARAMETERS name : str name of detector signal motor : `Mover` The independent coordinate motor_field : str name of `Mover` field center : float, optional location of maximum value, default=0 eta : float, optional 0 <= eta < 1.0: Lorentzian fraction, default=0.5 scale : float, optional scale >= 1 : scale factor, default=1 sigma : float, optional sigma > 0 : width, default=1 bkg : float, optional bkg >= 0 : constant background, default=0 noise : {'poisson', 'uniform', None} Add noise to the result. noise_multiplier : float Only relevant for 'uniform' noise. Multiply the random amount of noise by 'noise_multiplier' EXAMPLE :: from APS_BlueSky_tools.examples import SynPseudoVoigt motor = Mover('motor', {'motor': lambda x: x}, {'x': 0}) det = SynPseudoVoigt('det', motor, 'motor', center=0, eta=0.5, scale=1, sigma=1, bkg=0) EXAMPLE :: import numpy as np from APS_BlueSky_tools.examples import SynPseudoVoigt synthetic_pseudovoigt = SynPseudoVoigt( 'synthetic_pseudovoigt', m1, 'm1', center=-1.5 + 0.5*np.random.uniform(), eta=0.2 + 0.5*np.random.uniform(), sigma=0.001 + 0.05*np.random.uniform(), scale=1e5, bkg=0.01*np.random.uniform()) # RE(bp.scan([synthetic_pseudovoigt], m1, -2, 0, 219)) """ def __init__(self, name, motor, motor_field, center=0, eta=0.5, scale=1, sigma=1, bkg=0, noise=None, noise_multiplier=1, **kwargs): if eta < 0.0 or eta > 1.0: raise ValueError("eta={} must be between 0 and 1".format(eta)) if scale < 1.0: raise ValueError("scale must be >= 1") if sigma <= 0.0: raise ValueError("sigma must be > 0") if bkg < 0.0: raise ValueError("bkg must be >= 0") # remember these terms for later access by user self.name = name self.motor = motor self.center = center self.eta = eta self.scale = scale self.sigma = sigma self.bkg = bkg self.noise = noise self.noise_multiplier = noise_multiplier def f_lorentzian(x, gamma): #return gamma / np.pi / (x**2 + gamma**2) return 1 / np.pi / gamma / (1 + (x/gamma)**2) def f_gaussian(x, sigma): numerator = np.exp(-0.5 * (x / sigma) ** 2) denominator = sigma * np.sqrt(2 * np.pi) return numerator / denominator def pvoigt(): m = motor.read()[motor_field]['value'] g_max = f_gaussian(0, sigma) # peak normalization l_max = f_lorentzian(0, sigma) v = bkg if eta > 0: v += eta * f_lorentzian(m - center, sigma) / l_max if eta < 1: v += (1-eta) * f_gaussian(m - center, sigma) / g_max v *= scale if noise == 'poisson': v = int(np.random.poisson(np.round(v), 1)) elif noise == 'uniform': v += np.random.uniform(-1, 1) * noise_multiplier return v super().__init__(name=name, func=pvoigt, **kwargs)