"""This package contains irf items."""
from typing import List
from typing import Tuple
import numpy as np
from glotaran.model import ModelError
from glotaran.model import model_item
from glotaran.model import model_item_typed
from glotaran.parameter import Parameter
[docs]@model_item(has_type=True)
class IrfMeasured:
"""A measured IRF. The data must be supplied by the dataset."""
[docs]@model_item(
properties={
"center": List[Parameter],
"width": List[Parameter],
"scale": {"type": List[Parameter], "allow_none": True},
"shift": {"type": List[Parameter], "allow_none": True},
"normalize": {"type": bool, "default": True},
"backsweep": {"type": bool, "default": False},
"backsweep_period": {"type": Parameter, "allow_none": True},
},
has_type=True,
)
class IrfMultiGaussian:
"""
Represents a gaussian IRF.
One width and one center is a single gauss.
One center and multiple widths is a multiple gaussian.
Multiple center and multiple widths is Double-, Triple- , etc. Gaussian.
Parameters
----------
label:
label of the irf
center:
one or more center of the irf as parameter indices
width:
one or more widths of the gaussian as parameter index
center_dispersion_coefficients:
polynomial coefficients for the dispersion of the
center as list of parameter indices. None for no dispersion.
width_dispersion_coefficients:
polynomial coefficients for the dispersion of the
width as parameter indices. None for no dispersion.
"""
[docs] def parameter(
self, global_index: int, global_axis: np.ndarray
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, float, bool, float]:
"""Returns the properties of the irf with shift applied."""
centers = self.center if isinstance(self.center, list) else [self.center]
centers = np.asarray([c.value for c in centers])
widths = self.width if isinstance(self.width, list) else [self.width]
widths = np.asarray([w.value for w in widths])
len_centers = len(centers)
len_widths = len(widths)
if len_centers != len_widths:
if min(len_centers, len_widths) != 1:
raise ModelError(
f"len(centers) ({len_centers}) not equal "
f"len(widths) ({len_widths}) none of is 1."
)
if len_centers == 1:
centers = [centers[0] for _ in range(len_widths)]
else:
widths = [widths[0] for _ in range(len_centers)]
scales = self.scale if self.scale is not None else [1.0 for _ in centers]
scales = scales if isinstance(scales, list) else [scales]
scales = np.asarray(scales)
shift = 0
if self.shift is not None:
if global_index >= len(self.shift):
raise ModelError(
f"No shift parameter for index {global_index} "
f"({global_axis[global_index]}) in irf {self.label}"
)
shift = self.shift[global_index]
backsweep = self.backsweep
backsweep_period = self.backsweep_period.value if self.backsweep else 0
return centers, widths, scales, shift, backsweep, backsweep_period
[docs] def calculate(self, index: int, global_axis: np.ndarray, model_axis: np.ndarray) -> np.ndarray:
centers, widths, scales, _, _, _ = self.parameter(index, global_axis)
return sum(
scale * np.exp(-1 * (model_axis - center) ** 2 / (2 * width ** 2))
for center, width, scale in zip(centers, widths, scales)
)
[docs] def is_index_dependent(self):
return self.shift is not None
[docs]@model_item(
properties={
"center": Parameter,
"width": Parameter,
},
has_type=True,
)
class IrfGaussian(IrfMultiGaussian):
pass
[docs]@model_item(
properties={
"dispersion_center": {"type": Parameter, "allow_none": True},
"center_dispersion_coefficients": {"type": List[Parameter], "default": []},
"width_dispersion_coefficients": {"type": List[Parameter], "default": []},
"model_dispersion_with_wavenumber": {"type": bool, "default": False},
},
has_type=True,
)
class IrfSpectralMultiGaussian(IrfMultiGaussian):
"""
Represents a gaussian IRF.
One width and one center is a single gauss.
One center and multiple widths is a multiple gaussian.
Multiple center and multiple widths is Double-, Triple- , etc. Gaussian.
Parameters
----------
label:
label of the irf
center:
one or more center of the irf as parameter indices
width:
one or more widths of the gaussian as parameter index
center_dispersion_coefficients:
list of parameters with polynomial coefficients describing
the dispersion of the irf center location. None for no dispersion.
width_dispersion_coefficients:
list of parameters with polynomial coefficients describing
the dispersion of the width of the irf. None for no dispersion.
"""
[docs] def parameter(self, global_index: int, global_axis: np.ndarray):
"""Returns the properties of the irf with shift and dispersion applied."""
centers, widths, scale, shift, backsweep, backsweep_period = super().parameter(
global_index, global_axis
)
index = global_axis[global_index] if global_index is not None else None
if self.dispersion_center is not None:
dist = (
(1e3 / index - 1e3 / self.dispersion_center)
if self.model_dispersion_with_wavenumber
else (index - self.dispersion_center) / 100
)
if len(self.center_dispersion_coefficients) != 0:
if self.dispersion_center is None:
raise ModelError(f"No dispersion center defined for irf '{self.label}'")
for i, disp in enumerate(self.center_dispersion_coefficients):
centers += disp * np.power(dist, i + 1)
if len(self.width_dispersion_coefficients) != 0:
if self.dispersion_center is None:
raise ModelError(f"No dispersion center defined for irf '{self.label}'")
for i, disp in enumerate(self.width_dispersion_coefficients):
widths = widths + disp * np.power(dist, i + 1)
return centers, widths, scale, shift, backsweep, backsweep_period
[docs] def calculate_dispersion(self, axis):
dispersion = []
for index, _ in enumerate(axis):
center, _, _, _, _, _ = self.parameter(index, axis)
dispersion.append(center)
return np.asarray(dispersion).T
[docs] def is_index_dependent(self):
return super().is_index_dependent() or self.dispersion_center is not None
[docs]@model_item(
properties={
"center": Parameter,
"width": Parameter,
},
has_type=True,
)
class IrfSpectralGaussian(IrfSpectralMultiGaussian):
pass
[docs]@model_item_typed(
types={
"gaussian": IrfGaussian,
"multi-gaussian": IrfMultiGaussian,
"spectral-multi-gaussian": IrfSpectralMultiGaussian,
"spectral-gaussian": IrfSpectralGaussian,
}
)
class Irf:
"""Represents an IRF."""