Source code for apstools.devices.synth_pseudo_voigt

"""
Synthetic pseudo-Voigt function
+++++++++++++++++++++++++++++++++++++++

EXAMPLES:

.. code-block:: python
    :caption: Simple example of SynPseudoVoigt().
    :linenos:

    from apstools.devices import SynPseudoVoigt
    from ophyd.sim import motor
    det = SynPseudoVoigt('det', motor, 'motor',
        center=0, eta=0.5, scale=1, sigma=1, bkg=0)

    # scan the "det" peak with the "motor" positioner
    # RE(bp.scan([det], motor, -2, 2, 41))


.. code-block:: python
    :caption: Example of SynPseudoVoigt() with randomized values.
    :linenos:

    import numpy as np
    from apstools.devices 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())

    # scan the "synthetic_pseudovoigt" peak with the "m1" positioner
    # RE(bp.scan([synthetic_pseudovoigt], m1, -2, 0, 219))

.. autosummary::

   ~SynPseudoVoigt
"""

import numpy as np
import ophyd.sim


[docs]class SynPseudoVoigt(ophyd.sim.SynSignal): # lgtm [py/missing-call-to-init] """ Evaluate a point on a pseudo-Voigt based on the value of a motor. .. index:: Ophyd Signal; SynPseudoVoigt 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 positioner : The independent coordinate motor_field *str* : name of `motor` 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"`` or ``"uniform"`` or ``None`` : Add noise to the result. noise_multiplier *float* : Only relevant for 'uniform' noise. Multiply the random amount of noise by 'noise_multiplier' """ def __init__( # fmt: off self, name, motor, motor_field, center=0, eta=0.5, scale=1, sigma=1, bkg=0, noise=None, noise_multiplier=1, **kwargs # fmt: on ): 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.motor_field = motor_field 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()[self.motor_field]["value"] g_max = f_gaussian(0, self.sigma) # peak normalization l_max = f_lorentzian(0, self.sigma) v = self.bkg if eta > 0: v += self.eta * f_lorentzian(m - self.center, self.sigma) / l_max if eta < 1: v += (1 - self.eta) * f_gaussian(m - self.center, self.sigma) / g_max v *= self.scale if self.noise == "poisson": v = int(np.random.poisson(np.round(v), 1)) elif self.noise == "uniform": v += np.random.uniform(-1, 1) * self.noise_multiplier return v ophyd.sim.SynSignal.__init__(self, name=name, func=pvoigt, **kwargs)
[docs] def randomize_parameters(self, scale=100_000, bkg=0.01): """ Set random parameters. -1 <= center < 1, 0.001 <= sigma < 0.051, 95k <= scale <= 105k """ self.center = -1.0 + 2 * np.random.uniform() self.eta = 0.2 + 0.5 * np.random.uniform() self.sigma = 0.001 + 0.05 * np.random.uniform() self.scale = scale * (0.95 + 0.1 * np.random.uniform()) self.bkg = bkg * np.random.uniform() self.noise = "poisson"
# ----------------------------------------------------------------------------- # :author: Pete R. Jemian # :email: jemian@anl.gov # :copyright: (c) 2017-2023, UChicago Argonne, LLC # # Distributed under the terms of the Argonne National Laboratory Open Source License. # # The full license is in the file LICENSE.txt, distributed with this software. # -----------------------------------------------------------------------------