IMP logo
IMP Reference Guide  develop.6f18bfa751,2025/09/20
The Integrative Modeling Platform
lib/IMP/bff/spectroscopy/decay.py
1 import typing
2 import numpy as np
3 
4 import IMP.bff
5 
6 from typing import NamedTuple
7 
8 MAX_FLOAT = np.finfo(np.float64).max
9 MIN_FLOAT = np.finfo(np.float64).min
10 
11 
12 class DataSettings(NamedTuple):
13  data: np.ndarray = None
14  data_noise: np.ndarray = None
15  time_axis: np.array = None
16  irf_histogram: np.array = None
17  acquisition_time: float = MAX_FLOAT
18 
19 
20 class LifetimeSettings(NamedTuple):
21  lifetime_spectrum: np.array = np.array([], dtype=np.float64)
22  use_amplitude_threshold: bool = False
23  abs_lifetime_spectrum: bool = True
24  amplitude_threshold: float = MIN_FLOAT
25 
26 
27 class ConvolutionSettings(NamedTuple):
28  convolution_method: int = IMP.bff.DecayConvolution.FAST
29  excitation_period: float = 100.0
30  irf_shift_channels: float = 0.0
31  irf_background_counts: float = 0.0
32  start: int = 0
33  stop: int = -1
34  active: bool = True
35 
36 
37 class PatternSettings(NamedTuple):
38  constant_offset: float = 0.0
39  pattern: IMP.bff.DecayCurve = None
40  pattern_fraction: float = 0.0
41  start: int = 0
42  stop: int = -1
43  active: bool = True
44 
45 
46 class PileupSettings(NamedTuple):
47  pile_up_model: str = "coates"
48  repetition_rate: float = 10.0
49  instrument_dead_time: float = 120.0
50  start: int = 0
51  stop: int = -1
52  active: bool = True
53 
54 
55 class ScalingSettings(NamedTuple):
56  constant_offset: float = 0.0
57  start: int = 0
58  stop: int = -1
59  active: bool = True
60 
61 
62 class LinearizationSettings(NamedTuple):
63  linearization_table: IMP.bff.DecayCurve = None
64  start: int = 0
65  stop: int = -1
66  active: bool = True
67 
68 
69 class ScoreSettings(NamedTuple):
70  score_type: str = "default"
71  start: int = 0
72  stop: int = -1
73 
74 
75 class Decay(object):
76 
77  # These attributes are skipped when saving to JSON
78  _ignore_attributes = [
79  'irf_histogram',
80  # 'tttr_data',
81  # 'tttr_irf',
82  'linearization_table',
83  'corrected_irf',
84  'x',
85  'y',
86  'data_weights',
87  'number_of_photons'
88  ]
89 
90  _attributes = {
91  # VERSION
92  #####################
93  # TODO
94  # 'version': ("", "get_version"),
95  # Data
96  ############################
97  'data': ("_data", "y"),
98  'data_noise': ("_data", "ey"),
99  'model': ("_model", "y"),
100  'y': ("_model", "y"),
101  'x': ("_model", "x"),
102  'acquisition_time': ("_data", "acquisition_time"),
103  'time_axis': ("_data", "x"),
104  'irf_histogram': ("_irf", "y"),
105  'irf': ("_irf", "y"),
106  # 'tttr_data': ("_data", None, "set_tttr"),
107  # 'tttr_irf': ("_irf", None, "set_tttr"),
108  # LIFETIME SPECTRUM
109  ############################
110  'lifetime_spectrum': ("lifetime_handler", "get_lifetime_spectrum",
111  "set_lifetime_spectrum"),
112  'use_amplitude_threshold': ("lifetime_handler",
113  "use_amplitude_threshold"),
114  'abs_lifetime_spectrum': ("lifetime_handler", "abs_lifetime_spectrum"),
115  'amplitude_threshold': ("lifetime_handler", "amplitude_threshold"),
116  # CONVOLUTION
117  ############################
118  'corrected_irf': ("decay_convolution.corrected_irf", "y"),
119  'convolution_range': ("decay_convolution", "get_range", "set_range"),
120  'convolution_method': ("decay_convolution", "convolution_method"),
121  'excitation_period': ("decay_convolution", "excitation_period"),
122  'irf_shift_channels': ("decay_convolution", "irf_shift_channels"),
123  'irf_background_counts': ("decay_convolution",
124  "irf_background_counts"),
125  # Scatter
126  ############################
127  'scatter_fraction': ("decay_scatter", "pattern_fraction"),
128  # BACKGROUND
129  ############################
130  'constant_offset': ("", "constant_background"),
131  'pattern_fraction': ("decay_background", "pattern_fraction"),
132  # PILE-UP
133  #############################
134  'pile_up_model': ("decay_pileup", "pile_up_model"),
135  'instrument_dead_time': ("decay_pileup", "instrument_dead_time"),
136  'use_pile_up_correction': ("decay_pileup", "active"),
137  # SCALE
138  ############################
139  'scale_model_to_data': ("decay_scale", "active"),
140  'number_of_photons': ("decay_scale", "number_of_photons"),
141  # LINEARIZATION
142  ############################
143  'linearization_table': ("decay_linearization.data", "y"),
144  'linearization': ("decay_linearization.data", "y"),
145  'use_linearization': ("decay_linearization", "active"),
146  # SCORE
147  ###########################
148  'score_type': ("decay_score", "score_type"),
149  'score_range': ('', 'get_score_range', 'set_score_range'),
150  'score': ("decay_score", "score"),
151  'weighted_residuals': ("decay_score", "weighted_residuals")
152  }
153 
154  _methods = {
155  'get_mean_lifetime': ("decay_convolution", "get_mean_lifetime"),
156  'get_score': ("decay_score", "get_score")
157  }
158 
159  @staticmethod
160  def make_decay_curves(
161  data: np.ndarray = None,
162  data_noise: np.ndarray = None,
163  time_axis: np.array = None,
164  irf_histogram: np.array = None,
165  # tttr_data: tttrlib.TTTR = None,
166  # tttr_irf: tttrlib.TTTR = None,
167  # tttr_micro_time_coarsening: int = 1,
168  acquisition_time: float = MAX_FLOAT,
169  ) -> typing.Tuple[IMP.bff.DecayCurve, IMP.bff.DecayCurve]:
170  # Data
171  if time_axis is None:
172  time_axis = np.array([], np.float64)
173  if data is None:
174  data = np.array([], np.float64)
175  # IRF
176  if irf_histogram is None:
177  irf_histogram = np.zeros_like(data)
178  if len(irf_histogram) > 0:
179  irf_histogram[0] = 1.0
180  decay = IMP.bff.DecayCurve(x=time_axis, y=data,
181  acquisition_time=acquisition_time)
182  irf = IMP.bff.DecayCurve(x=time_axis, y=irf_histogram)
183  if data_noise is not None:
184  decay.set_ey(data_noise)
185  return decay, irf
186 
187  def __init__(
188  self,
189  data_settings=None, # type: DataSettings
190  lifetime_settings=None, # type: LifetimeSettings
191  convolution_settings=None, # type: ConvolutionSettings
192  scatter_settings=None, # type: PatternSettings
193  background_settings=None, # type: PatternSettings
194  pileup_settings=None, # type: PileupSettings
195  scaling_settings=None, # type: ScalingSettings
196  linearization_settings=None, # type: LinearizationSettings
197  score_settings=None # type: ScoreSettings
198  ):
199  super().__init__()
200  if data_settings is None:
201  data_settings = DataSettings()
202  if lifetime_settings is None:
203  lifetime_settings = LifetimeSettings()
204  if convolution_settings is None:
205  convolution_settings = ConvolutionSettings()
206  if background_settings is None:
207  background_settings = PatternSettings()
208  if scatter_settings is None:
209  scatter_settings = PatternSettings()
210  if pileup_settings is None:
211  pileup_settings = PileupSettings()
212  if linearization_settings is None:
213  linearization_settings = LinearizationSettings()
214  if scaling_settings is None:
215  scaling_settings = ScalingSettings()
216  if score_settings is None:
217  score_settings = ScoreSettings()
218 
219  # init decay curves
220  data, irf = self.make_decay_curves(*data_settings)
221  model = IMP.bff.DecayCurve(data.x)
222 
223  lifetime_handler = IMP.bff.DecayLifetimeHandler(*lifetime_settings)
224  decay_convolution = IMP.bff.DecayConvolution(
225  lifetime_handler, irf, *convolution_settings)
226  decay_scatter = IMP.bff.DecayPattern(*scatter_settings)
227  decay_scatter.data = irf
228  decay_background = IMP.bff.DecayPattern(*background_settings)
229  decay_pileup = IMP.bff.DecayPileup(data, *pileup_settings)
230  decay_linearization = IMP.bff.DecayLinearization(
231  *linearization_settings)
232  decay_scale = IMP.bff.DecayScale(data, *scaling_settings)
233  decay_score = IMP.bff.DecayScore(model, data, *score_settings)
234 
235  self._data, self._irf, self._model = data, irf, model
236  self.lifetime_handler = lifetime_handler
237  self.decay_convolution = decay_convolution
238  self.decay_scatter = decay_scatter
239  self.decay_pileup = decay_pileup
240  self.decay_linearization = decay_linearization
241  self.decay_scale = decay_scale
242  self.decay_background = decay_background
243  self.decay_score = decay_score
244 
245  self._decay_modifier = [
246  self.decay_convolution,
247  self.decay_scatter,
248  # self.decay_pileup, # TODO: seems cn
249  self.decay_linearization,
250  self.decay_scale,
251  self.decay_background,
252  ]
253 
254  def load(self, *args, **kwargs):
255  super().load(*args, **kwargs)
256  self._model.x = self._data.x
257  self._irf.x = self._data.x
258 
259  def update(self):
260  dc = self
261  for dm in self._decay_modifier:
262  dm.add(dc._model)
263 
264  @property
265  def score(self, *args, **kwargs):
266  self.update()
267  return self.decay_score.get_score(*args, **kwargs)
268 
269  @property
270  def mean_lifetime(self):
271  return self.decay_convolution.get_mean_lifetime(self._data)
272 
273  @property
274  def constant_background(self):
275  return self.decay_scale.constant_background
276 
277  @constant_background.setter
278  def constant_background(self, v):
279  self.decay_scale.constant_background = v
280  self.decay_background.constant_offset = v
281 
282  def get_score_range(self):
283  return self.decay_score.get_range(self._data)
284 
285  def set_score_range(self, start_stop):
286  for dm in self._decay_modifier:
287  dm.set_range(start_stop)
288  self.decay_score.set_range(start_stop)
A decorator that adds pile-up effects to a DecayCurve object.
Definition: DecayPileup.h:34
Compute convolved decay.
A decay modifier to apply linearization to a DecayCurve.
A class for scaling a DecayCurve by a constant factor and subtracting a constant background value...
Definition: DecayScale.h:34
Class for scoring model fluorescence decay.
Definition: DecayScore.h:34
Class for fluorescence decay curves.
Definition: DecayCurve.h:38
The DecayPattern class represents a decay pattern with a constant offset and a background pattern...
Definition: DecayPattern.h:26
Bayesian Fluorescence Framework.
Store and handle lifetime spectra.