Source code for bio_rtd.pdf

"""Wrappers for probability distribution functions.

This module contains subclasses of :class:`bio_rtd.core.PDF`.

The integral of a probability distribution function (PDF) over time has
a value of 1.

Examples
--------
>>> t = _np.linspace(0, 100, 1001)
>>> dt = t[1]
>>> pdf = GaussianFixedDispersion(t, 0.2)
>>> pdf.trim_and_normalize = False
>>> pdf.update_pdf(rt_mean=40)
>>> p = pdf.get_p()
>>> print(round(p.sum() * dt, 8))
1.0
>>> t[p.argmax()]
40.0

"""

__version__ = '0.7.1'
__author__ = 'Jure Sencar'

import numpy as _np

from bio_rtd.core import PDF as _PDF
from bio_rtd import peak_shapes as _peak_shapes


[docs]class GaussianFixedDispersion(_PDF): """Gaussian PDF with fixed dispersion. Parameters ---------- dispersion_index Dispersion index. Dispersion index is defined as `sigma * sigma / rt_mean`. Where `rt_mean` is a mean residence time and `sigma` is a standard deviation on time scale (not volume). cutoff Cutoff limit for trimming front and end tailing. Cutoff limit is relative to the peak max value. pdf_id Unique identifier. Default = "GaussianFixedDispersion". See Also -------- :class:`rtd_lib.core.PDF` Examples ------- >>> t = _np.linspace(0, 100, 1001) >>> dt = t[1] >>> pdf = GaussianFixedDispersion(t, dispersion_index=0.2) >>> pdf.trim_and_normalize = False >>> pdf.update_pdf(rt_mean=40) >>> p = pdf.get_p() >>> print(round(p.sum() * dt, 4)) 1.0 >>> t[p.argmax()] 40.0 """ POSSIBLE_KEY_GROUPS = [['f', 'v_void'], ['rt_mean']] OPTIONAL_KEYS = [] def __init__(self, t: _np.ndarray, dispersion_index: float, cutoff=0.0001, pdf_id: str = "GaussianFixedDispersion"): super().__init__(t, pdf_id) self.dispersion_index = dispersion_index """Dispersion index. Dispersion index is defined as `sigma * sigma / rt_mean`. Where `rt_mean` is a mean residence time and `sigma` is a standard deviation. """ self.cutoff_relative_to_max = cutoff """Cutoff limit for trimming front and end tailing. Cutoff limit is relative to the peak max value. """ def _calc_pdf(self, kw_pars: dict) -> _np.ndarray: # Get rt_mean from parameters. rt_mean = kw_pars['rt_mean'] \ if 'rt_mean' in kw_pars.keys() \ else kw_pars['v_void'] / kw_pars['f'] sigma = (rt_mean * self.dispersion_index) ** 0.5 # Calc probability distribution `p`. if self.trim_and_normalize: t_max = rt_mean + sigma * \ _np.sqrt(-2 * _np.log(self.cutoff_relative_to_max)) i_max = min(_np.ceil(t_max / self._dt) + 1, self._t_steps_max) else: i_max = self._t_steps_max t = _np.arange(i_max) * self._dt p = _peak_shapes.gaussian(t, rt_mean, sigma, self.log) return p
[docs]class GaussianFixedRelativeWidth(_PDF): """Gaussian PDF with fixed relative peak width. Parameters ---------- relative_sigma Relative sigma. Relative sigma is defined as `sigma / rt_mean`. Where `rt_mean` is a mean residence time and `sigma` is a standard deviation. cutoff Cutoff limit for trimming front and end tailing. Cutoff limit is relative to the peak max value. pdf_id Unique identifier. Default = "GaussianFixedRelativeWidth". See Also -------- :class:`rtd_lib.core.PDF` Examples ------- >>> t = _np.linspace(0, 100, 1001) >>> dt = t[1] >>> pdf = GaussianFixedRelativeWidth(t, relative_sigma=0.15) >>> pdf.trim_and_normalize = False >>> pdf.update_pdf(rt_mean=40) >>> p = pdf.get_p() >>> print(round(p.sum() * dt, 4)) 1.0 >>> t[p.argmax()] 40.0 """ POSSIBLE_KEY_GROUPS = [['f', 'v_void'], ['rt_mean']] OPTIONAL_KEYS = [] def __init__(self, t: _np.array, relative_sigma: float, cutoff=0.0001, pdf_id: str = "GaussianFixedRelativeWidth"): super().__init__(t, pdf_id) self.relative_sigma = relative_sigma """Relative sigma. Relative sigma is defined as `sigma / rt_mean`. Where `rt_mean` is a mean residence time and `sigma` is a standard deviation. """ self.cutoff_relative_to_max = cutoff """Cutoff limit for trimming front and end tailing. Cutoff limit is relative to the peak max value. """ def _calc_pdf(self, kw_pars: dict) -> _np.ndarray: # Get `rt_mean` from parameters. rt_mean = kw_pars['rt_mean'] if 'rt_mean' in kw_pars.keys() \ else kw_pars['v_void'] / kw_pars['f'] # Calc `sigma`. sigma = rt_mean * self.relative_sigma # Calc probability distribution (`p`). t_max = sigma * _np.sqrt(-2 * _np.log(self.cutoff_relative_to_max)) \ + rt_mean t_max = min(_np.ceil(t_max / self._dt), self._t_steps_max) * self._dt p = _peak_shapes.gaussian( _np.arange(0, t_max, self._dt), rt_mean, sigma, self.log) return p
[docs]class ExpModGaussianFixedDispersion(_PDF): """Exponentially Modified Gaussian PDF with fixed dispersion. Parameters ---------- dispersion_index Dispersion index of Gaussian part. Dispersion index is defined as `sigma * sigma / rt_mean`. Where `rt_mean` is a mean residence time and `sigma` is a standard deviation on time scale (not volume). skew Rate of exponential part. pdf_id Unique identifier. Default = "ExpModGaussianFixedDispersion". See Also -------- :class:`rtd_lib.core.PDF` Examples ------- >>> t = _np.linspace(0, 100, 1001) >>> dt = t[1] >>> dispersion_index = 0.2 >>> skew = 0.5 >>> pdf = ExpModGaussianFixedDispersion(t, dispersion_index, skew) >>> pdf.trim_and_normalize = False >>> pdf.update_pdf(rt_mean=40) >>> p = pdf.get_p() >>> print(round(p.sum() * dt, 8)) 1.0 >>> t[p.argmax()] # position of peak max 39.6 >>> print(round((p * t[:p.size]).sum() * dt, 3)) # 1st momentum 40.0 """ POSSIBLE_KEY_GROUPS = [['f', 'v_void'], ['rt_mean']] OPTIONAL_KEYS = ['skew'] def __init__(self, t: _np.array, dispersion_index: float, skew: float, pdf_id: str = "ExpModGaussianFixedDispersion"): super().__init__(t, pdf_id) self.dispersion_index = dispersion_index """Dispersion index for Gaussian part. Dispersion index is defined as `sigma * sigma / rt_mean`. Where `rt_mean` is a mean residence time and `sigma` is a standard deviation. """ self.skew = skew """Rate of exponential part.""" def _calc_pdf(self, kw_pars: dict) -> _np.ndarray: # Get `rt_mean` from parameters. rt_mean = kw_pars['rt_mean'] if 'rt_mean' in kw_pars.keys() \ else kw_pars['v_void'] / kw_pars['f'] # Get `skew` from parameters if provided. skew = kw_pars['skew'] if 'skew' in kw_pars.keys() else self.skew # Calc `sigma`. sigma = (rt_mean * self.dispersion_index) ** 0.5 # Calc probability distribution (`p`). t = _np.arange(0, self._t_steps_max * self._dt, self._dt) p = _peak_shapes.emg(t, rt_mean, sigma, skew, self.log) return p
[docs]class ExpModGaussianFixedRelativeWidth(_PDF): """Exponentially Modified Gaussian PDF with fixed relative sigma. Parameters ---------- sigma_relative Relative sigma for Gaussian part. Relative sigma is defined as `sigma / rt_mean`. Where `rt_mean` is a mean residence time and `sigma` is a standard deviation. tau_relative Relative characteristic time of exponential part. It is defined as 1 / (`skew` * `rt_mean`). pdf_id Unique identifier. Default = "ExpModGaussianFixedRelativeWidth". See Also -------- :class:`rtd_lib.core.PDF` Examples ------- >>> t = _np.linspace(0, 100, 1001) >>> dt = t[1] >>> sigma_relative = 0.15 >>> skew = 0.5 >>> pdf = ExpModGaussianFixedDispersion(t, sigma_relative, skew) >>> pdf.trim_and_normalize = False >>> pdf.update_pdf(rt_mean=40) >>> p = pdf.get_p() >>> print(round(p.sum() * dt, 8)) 1.0 >>> t[p.argmax()] # position of peak max 39.5 >>> print(round((p * t[:p.size]).sum() * dt, 2)) # 1st momentum 40.0 """ POSSIBLE_KEY_GROUPS = [['rt_mean'], ['f', 'v_void']] OPTIONAL_KEYS = ['skew'] def __init__(self, t: _np.array, sigma_relative: float, tau_relative: float, pdf_id: str = "ExpModGaussianFixedRelativeWidth"): super().__init__(t, pdf_id) self.sigma_relative = sigma_relative """Relative sigma for Gaussian part. Relative sigma is defined as `sigma / rt_mean`. Where `rt_mean` is a mean residence time and `sigma` is a standard deviation. """ self.tau_relative = tau_relative """Relative characteristic time of exponential part. Relative characteristic time is defined as 1 / (`skew` * `rt_mean`). Where `rt_mean` is a mean residence time and `skew` is the rate of the exponential part. """ def _calc_pdf(self, kw_pars: dict) -> _np.ndarray: # Get `rt_mean` from parameters. rt_mean = kw_pars['rt_mean'] if 'rt_mean' in kw_pars.keys() \ else kw_pars['v_void'] / kw_pars['f'] # Get `skew` from parameters if provided. skew = kw_pars['skew'] if 'skew' in kw_pars.keys() \ else 1 / self.tau_relative / rt_mean # Calc `sigma`. sigma = rt_mean * self.sigma_relative # Calc probability distribution (`p`). t = _np.arange(0, self._t_steps_max * self._dt, self._dt) p = _peak_shapes.emg(t, rt_mean, sigma, skew, self.log) return p
[docs]class TanksInSeries(_PDF): """Tanks in series PDF. `rt_mean` means flow-through time through entire unit operation (all tanks). For `n_tanks` == 1, the distribution becomes exponential drop. Parameters ---------- n_tanks Number of tanks. pdf_id Unique identifier. Default = "TanksInSeries". See Also -------- :class:`rtd_lib.core.PDF` Examples ------- >>> t = _np.linspace(0, 100, 1001) >>> dt = t[1] >>> sigma_relative = 0.15 >>> skew = 0.5 >>> pdf = TanksInSeries(t, n_tanks=5) >>> pdf.update_pdf(rt_mean=10) >>> pdf.trim_and_normalize = False >>> p = pdf.get_p() >>> print(round(p.sum() * dt, 8)) 1.0 >>> t[p.argmax()] # position of peak max 8.0 >>> print(round((p * t[:p.size]).sum() * dt, 2)) # 1st momentum 10.0 >>> pdf = TanksInSeries(t, n_tanks=1) >>> pdf.trim_and_normalize = False >>> pdf.update_pdf(rt_mean=10) >>> p = pdf.get_p() >>> print(round(p.sum() * dt, 2)) 1.0 >>> t[p.argmax()] # position of peak max 0.0 >>> print(round((p * t[:p.size]).sum() * dt, 1)) # 1st momentum 10.0 """ POSSIBLE_KEY_GROUPS = [['rt_mean'], ['f', 'v_void']] OPTIONAL_KEYS = ['n_tanks'] def __init__(self, t: _np.array, n_tanks: float, pdf_id: str = "TanksInSeries"): super().__init__(t, pdf_id) self.n_tanks = n_tanks """Number of tanks.""" self.allow_open_end: bool = False """Prevent warnings and errors if the pdf does not fit on `t`. Default: `False` If `True`, no warnings or errors are reported in case the distribution does not fit on provided time vector. """ def _calc_pdf(self, kw_pars: dict) -> _np.ndarray: # Get `rt_mean` from parameters. rt_mean = kw_pars['rt_mean'] if 'rt_mean' in kw_pars.keys() \ else kw_pars['v_void'] / kw_pars['f'] # Get`n_tanks` parameter if provided. n_tanks = kw_pars['n_tanks'] if 'n_tanks' in kw_pars.keys() \ else self.n_tanks # Calc probability distribution (`p`). t = _np.arange(0, self._t_steps_max * self._dt, self._dt) p = _peak_shapes.tanks_in_series(t, rt_mean, n_tanks, self.log, self.allow_open_end) return p