phasorpy 0.4__cp313-cp313-win_amd64.whl → 0.6__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,559 @@
1
+ """PhasorPlotFret class."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __all__ = ['PhasorPlotFret']
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from .._typing import Any, NDArray
11
+
12
+ from matplotlib.axes import Axes
13
+
14
+ import numpy
15
+ from matplotlib import pyplot
16
+ from matplotlib.lines import Line2D
17
+ from matplotlib.widgets import Slider
18
+
19
+ from .._utils import update_kwargs
20
+ from ..phasor import (
21
+ phasor_from_fret_acceptor,
22
+ phasor_from_fret_donor,
23
+ phasor_from_lifetime,
24
+ phasor_semicircle,
25
+ phasor_to_polar,
26
+ phasor_transform,
27
+ )
28
+ from ._phasorplot import PhasorPlot, SemicircleTicks, _semicircle_ticks
29
+
30
+
31
+ class PhasorPlotFret(PhasorPlot):
32
+ """FRET phasor plot.
33
+
34
+ Plot Förster Resonance Energy Transfer efficiency trajectories
35
+ of donor and acceptor channels in phasor space.
36
+
37
+ Parameters
38
+ ----------
39
+ frequency : array_like
40
+ Laser pulse or modulation frequency in MHz.
41
+ donor_lifetime : array_like
42
+ Lifetime of donor without FRET in ns.
43
+ acceptor_lifetime : array_like
44
+ Lifetime of acceptor in ns.
45
+ fret_efficiency : array_like, optional, default 0
46
+ FRET efficiency in range [0, 1].
47
+ donor_fretting : array_like, optional, default 1
48
+ Fraction of donors participating in FRET. Range [0, 1].
49
+ donor_bleedthrough : array_like, optional, default 0
50
+ Weight of donor fluorescence in acceptor channel
51
+ relative to fluorescence of fully sensitized acceptor.
52
+ A weight of 1 means the fluorescence from donor and fully sensitized
53
+ acceptor are equal.
54
+ The background in the donor channel does not bleed through.
55
+ acceptor_bleedthrough : array_like, optional, default 0
56
+ Weight of fluorescence from directly excited acceptor
57
+ relative to fluorescence of fully sensitized acceptor.
58
+ A weight of 1 means the fluorescence from directly excited acceptor
59
+ and fully sensitized acceptor are equal.
60
+ acceptor_background : array_like, optional, default 0
61
+ Weight of background fluorescence in acceptor channel
62
+ relative to fluorescence of fully sensitized acceptor.
63
+ A weight of 1 means the fluorescence of background and fully
64
+ sensitized acceptor are equal.
65
+ donor_background : array_like, optional, default 0
66
+ Weight of background fluorescence in donor channel
67
+ relative to fluorescence of donor without FRET.
68
+ A weight of 1 means the fluorescence of background and donor
69
+ without FRET are equal.
70
+ background_real : array_like, optional, default 0
71
+ Real component of background fluorescence phasor coordinate
72
+ at `frequency`.
73
+ background_imag : array_like, optional, default 0
74
+ Imaginary component of background fluorescence phasor coordinate
75
+ at `frequency`.
76
+ ax : matplotlib axes, optional
77
+ Matplotlib axes used for plotting.
78
+ By default, a new subplot axes is created.
79
+ Cannot be used with `interactive` mode.
80
+ interactive : bool, optional, default: False
81
+ Use matplotlib slider widgets to interactively control parameters.
82
+ **kwargs
83
+ Additional parameters passed to :py:class:`phasorpy.plot.PhasorPlot`.
84
+
85
+ See Also
86
+ --------
87
+ phasorpy.phasor.phasor_from_fret_donor
88
+ phasorpy.phasor.phasor_from_fret_acceptor
89
+ :ref:`sphx_glr_tutorials_api_phasorpy_fret.py`
90
+
91
+ """
92
+
93
+ _fret_efficiencies: NDArray[Any]
94
+
95
+ _frequency_slider: Slider
96
+ _donor_lifetime_slider: Slider
97
+ _acceptor_lifetime_slider: Slider
98
+ _fret_efficiency_slider: Slider
99
+ _donor_fretting_slider: Slider
100
+ _donor_bleedthrough_slider: Slider
101
+ _acceptor_bleedthrough_slider: Slider
102
+ _acceptor_background_slider: Slider
103
+ _donor_background_slider: Slider
104
+ _background_real_slider: Slider
105
+ _background_imag_slider: Slider
106
+
107
+ _donor_line: Line2D
108
+ _donor_only_line: Line2D
109
+ _donor_fret_line: Line2D
110
+ _donor_trajectory_line: Line2D
111
+ _donor_semicircle_line: Line2D
112
+ _donor_donor_line: Line2D
113
+ _donor_background_line: Line2D
114
+ _acceptor_line: Line2D
115
+ _acceptor_only_line: Line2D
116
+ _acceptor_trajectory_line: Line2D
117
+ _acceptor_semicircle_line: Line2D
118
+ _acceptor_background_line: Line2D
119
+ _background_line: Line2D
120
+
121
+ _donor_semicircle_ticks: SemicircleTicks | None
122
+
123
+ def __init__(
124
+ self,
125
+ *,
126
+ frequency: float = 60.0,
127
+ donor_lifetime: float = 4.2,
128
+ acceptor_lifetime: float = 3.0,
129
+ fret_efficiency: float = 0.5,
130
+ donor_fretting: float = 1.0,
131
+ donor_bleedthrough: float = 0.0,
132
+ acceptor_bleedthrough: float = 0.0,
133
+ acceptor_background: float = 0.0,
134
+ donor_background: float = 0.0,
135
+ background_real: float = 0.0,
136
+ background_imag: float = 0.0,
137
+ ax: Axes | None = None,
138
+ interactive: bool = False,
139
+ **kwargs: Any,
140
+ ) -> None:
141
+ update_kwargs(
142
+ kwargs,
143
+ title='PhasorPy FRET phasor plot',
144
+ xlim=[-0.2, 1.1],
145
+ ylim=[-0.1, 0.8],
146
+ )
147
+ kwargs['allquadrants'] = False
148
+ kwargs['grid'] = False
149
+
150
+ if ax is not None:
151
+ interactive = False
152
+ else:
153
+ fig = pyplot.figure()
154
+ ax = fig.add_subplot()
155
+ if interactive:
156
+ w, h = fig.get_size_inches()
157
+ fig.set_size_inches(w, h * 1.66)
158
+ fig.subplots_adjust(bottom=0.45)
159
+ fcm = fig.canvas.manager
160
+ if fcm is not None:
161
+ fcm.set_window_title(kwargs['title'])
162
+
163
+ super().__init__(ax=ax, **kwargs)
164
+
165
+ self._fret_efficiencies = numpy.linspace(0.0, 1.0, 101)
166
+
167
+ donor_real, donor_imag = phasor_from_lifetime(
168
+ frequency, donor_lifetime
169
+ )
170
+ donor_fret_real, donor_fret_imag = phasor_from_lifetime(
171
+ frequency, donor_lifetime * (1.0 - fret_efficiency)
172
+ )
173
+ acceptor_real, acceptor_imag = phasor_from_lifetime(
174
+ frequency, acceptor_lifetime
175
+ )
176
+ donor_trajectory_real, donor_trajectory_imag = phasor_from_fret_donor(
177
+ frequency,
178
+ donor_lifetime,
179
+ fret_efficiency=self._fret_efficiencies,
180
+ donor_fretting=donor_fretting,
181
+ donor_background=donor_background,
182
+ background_real=background_real,
183
+ background_imag=background_imag,
184
+ )
185
+ (
186
+ acceptor_trajectory_real,
187
+ acceptor_trajectory_imag,
188
+ ) = phasor_from_fret_acceptor(
189
+ frequency,
190
+ donor_lifetime,
191
+ acceptor_lifetime,
192
+ fret_efficiency=self._fret_efficiencies,
193
+ donor_fretting=donor_fretting,
194
+ donor_bleedthrough=donor_bleedthrough,
195
+ acceptor_bleedthrough=acceptor_bleedthrough,
196
+ acceptor_background=acceptor_background,
197
+ background_real=background_real,
198
+ background_imag=background_imag,
199
+ )
200
+
201
+ # add plots
202
+ lines = self.semicircle(frequency=frequency)
203
+ self._donor_semicircle_line = lines[0]
204
+ self._donor_semicircle_ticks = self._semicircle_ticks
205
+
206
+ lines = self.semicircle(
207
+ phasor_reference=(float(acceptor_real), float(acceptor_imag)),
208
+ use_lines=True,
209
+ )
210
+ self._acceptor_semicircle_line = lines[0]
211
+
212
+ if donor_fretting < 1.0 and donor_background == 0.0:
213
+ lines = self.line(
214
+ [donor_real, donor_fret_real],
215
+ [donor_imag, donor_fret_imag],
216
+ )
217
+ else:
218
+ lines = self.line([0.0, 0.0], [0.0, 0.0])
219
+ self._donor_donor_line = lines[0]
220
+
221
+ if acceptor_background > 0.0:
222
+ lines = self.line(
223
+ [float(acceptor_real), float(background_real)],
224
+ [float(acceptor_imag), float(background_imag)],
225
+ )
226
+ else:
227
+ lines = self.line([0.0, 0.0], [0.0, 0.0])
228
+ self._acceptor_background_line = lines[0]
229
+
230
+ if donor_background > 0.0:
231
+ lines = self.line(
232
+ [float(donor_real), float(background_real)],
233
+ [float(donor_imag), float(background_imag)],
234
+ )
235
+ else:
236
+ lines = self.line([0.0, 0.0], [0.0, 0.0])
237
+ self._donor_background_line = lines[0]
238
+
239
+ lines = self.plot(
240
+ donor_trajectory_real,
241
+ donor_trajectory_imag,
242
+ '-',
243
+ color='tab:green',
244
+ )
245
+ self._donor_trajectory_line = lines[0]
246
+
247
+ lines = self.plot(
248
+ acceptor_trajectory_real,
249
+ acceptor_trajectory_imag,
250
+ '-',
251
+ color='tab:red',
252
+ )
253
+ self._acceptor_trajectory_line = lines[0]
254
+
255
+ lines = self.plot(
256
+ donor_real,
257
+ donor_imag,
258
+ '.',
259
+ color='tab:green',
260
+ )
261
+ self._donor_only_line = lines[0]
262
+
263
+ lines = self.plot(
264
+ donor_real,
265
+ donor_imag,
266
+ '.',
267
+ color='tab:green',
268
+ )
269
+ self._donor_fret_line = lines[0]
270
+
271
+ lines = self.plot(
272
+ acceptor_real,
273
+ acceptor_imag,
274
+ '.',
275
+ color='tab:red',
276
+ )
277
+ self._acceptor_only_line = lines[0]
278
+
279
+ lines = self.plot(
280
+ donor_trajectory_real[int(fret_efficiency * 100.0)],
281
+ donor_trajectory_imag[int(fret_efficiency * 100.0)],
282
+ 'o',
283
+ color='tab:green',
284
+ label='Donor',
285
+ )
286
+ self._donor_line = lines[0]
287
+
288
+ lines = self.plot(
289
+ acceptor_trajectory_real[int(fret_efficiency * 100.0)],
290
+ acceptor_trajectory_imag[int(fret_efficiency * 100.0)],
291
+ 'o',
292
+ color='tab:red',
293
+ label='Acceptor',
294
+ )
295
+ self._acceptor_line = lines[0]
296
+
297
+ lines = self.plot(
298
+ background_real,
299
+ background_imag,
300
+ 'o',
301
+ color='black',
302
+ label='Background',
303
+ )
304
+ self._background_line = lines[0]
305
+
306
+ if not interactive:
307
+ return
308
+
309
+ # add sliders
310
+ axes = []
311
+ for i in range(11):
312
+ axes.append(fig.add_axes((0.33, 0.05 + i * 0.03, 0.45, 0.01)))
313
+
314
+ self._frequency_slider = Slider(
315
+ ax=axes[10],
316
+ label='Frequency ',
317
+ valfmt=' %.0f MHz',
318
+ valmin=10,
319
+ valmax=200,
320
+ valstep=1,
321
+ valinit=frequency,
322
+ )
323
+ self._frequency_slider.on_changed(self._on_semicircle_changed)
324
+
325
+ self._donor_lifetime_slider = Slider(
326
+ ax=axes[9],
327
+ label='Donor lifetime ',
328
+ valfmt=' %.1f ns',
329
+ valmin=0.1,
330
+ valmax=16.0,
331
+ valstep=0.1,
332
+ valinit=donor_lifetime,
333
+ # facecolor='tab:green',
334
+ handle_style={'edgecolor': 'tab:green'},
335
+ )
336
+ self._donor_lifetime_slider.on_changed(self._on_changed)
337
+
338
+ self._acceptor_lifetime_slider = Slider(
339
+ ax=axes[8],
340
+ label='Acceptor lifetime ',
341
+ valfmt=' %.1f ns',
342
+ valmin=0.1,
343
+ valmax=16.0,
344
+ valstep=0.1,
345
+ valinit=acceptor_lifetime,
346
+ # facecolor='tab:red',
347
+ handle_style={'edgecolor': 'tab:red'},
348
+ )
349
+ self._acceptor_lifetime_slider.on_changed(self._on_semicircle_changed)
350
+
351
+ self._fret_efficiency_slider = Slider(
352
+ ax=axes[7],
353
+ label='FRET efficiency ',
354
+ valfmt=' %.2f',
355
+ valmin=0.0,
356
+ valmax=1.0,
357
+ valstep=0.01,
358
+ valinit=fret_efficiency,
359
+ )
360
+ self._fret_efficiency_slider.on_changed(self._on_changed)
361
+
362
+ self._donor_fretting_slider = Slider(
363
+ ax=axes[6],
364
+ label='Donors fretting ',
365
+ valfmt=' %.2f',
366
+ valmin=0.0,
367
+ valmax=1.0,
368
+ valstep=0.01,
369
+ valinit=donor_fretting,
370
+ # facecolor='tab:green',
371
+ handle_style={'edgecolor': 'tab:green'},
372
+ )
373
+ self._donor_fretting_slider.on_changed(self._on_changed)
374
+
375
+ self._donor_bleedthrough_slider = Slider(
376
+ ax=axes[5],
377
+ label='Donor bleedthrough ',
378
+ valfmt=' %.2f',
379
+ valmin=0.0,
380
+ valmax=5.0,
381
+ valstep=0.01,
382
+ valinit=donor_bleedthrough,
383
+ # facecolor='tab:red',
384
+ handle_style={'edgecolor': 'tab:red'},
385
+ )
386
+ self._donor_bleedthrough_slider.on_changed(self._on_changed)
387
+
388
+ self._acceptor_bleedthrough_slider = Slider(
389
+ ax=axes[4],
390
+ label='Acceptor bleedthrough ',
391
+ valfmt=' %.2f',
392
+ valmin=0.0,
393
+ valmax=5.0,
394
+ valstep=0.01,
395
+ valinit=acceptor_bleedthrough,
396
+ # facecolor='tab:red',
397
+ handle_style={'edgecolor': 'tab:red'},
398
+ )
399
+ self._acceptor_bleedthrough_slider.on_changed(self._on_changed)
400
+
401
+ self._acceptor_background_slider = Slider(
402
+ ax=axes[3],
403
+ label='Acceptor background ',
404
+ valfmt=' %.2f',
405
+ valmin=0.0,
406
+ valmax=5.0,
407
+ valstep=0.01,
408
+ valinit=acceptor_background,
409
+ # facecolor='tab:red',
410
+ handle_style={'edgecolor': 'tab:red'},
411
+ )
412
+ self._acceptor_background_slider.on_changed(self._on_changed)
413
+
414
+ self._donor_background_slider = Slider(
415
+ ax=axes[2],
416
+ label='Donor background ',
417
+ valfmt=' %.2f',
418
+ valmin=0.0,
419
+ valmax=5.0,
420
+ valstep=0.01,
421
+ valinit=donor_background,
422
+ # facecolor='tab:green',
423
+ handle_style={'edgecolor': 'tab:green'},
424
+ )
425
+ self._donor_background_slider.on_changed(self._on_changed)
426
+
427
+ self._background_real_slider = Slider(
428
+ ax=axes[1],
429
+ label='Background real ',
430
+ valfmt=' %.2f',
431
+ valmin=0.0,
432
+ valmax=1.0,
433
+ valstep=0.01,
434
+ valinit=background_real,
435
+ )
436
+ self._background_real_slider.on_changed(self._on_changed)
437
+
438
+ self._background_imag_slider = Slider(
439
+ ax=axes[0],
440
+ label='Background imag ',
441
+ valfmt=' %.2f',
442
+ valmin=0.0,
443
+ valmax=0.6,
444
+ valstep=0.01,
445
+ valinit=background_imag,
446
+ )
447
+ self._background_imag_slider.on_changed(self._on_changed)
448
+
449
+ def _on_semicircle_changed(self, value: Any) -> None:
450
+ """Callback function to update semicircles."""
451
+ self._frequency = frequency = self._frequency_slider.val
452
+ acceptor_lifetime = self._acceptor_lifetime_slider.val
453
+ if self._donor_semicircle_ticks is not None:
454
+ lifetime, labels = _semicircle_ticks(frequency)
455
+ self._donor_semicircle_ticks.labels = labels
456
+ self._donor_semicircle_line.set_data(
457
+ *phasor_transform(*phasor_from_lifetime(frequency, lifetime))
458
+ )
459
+ self._acceptor_semicircle_line.set_data(
460
+ *phasor_transform(
461
+ *phasor_semicircle(),
462
+ *phasor_to_polar(
463
+ *phasor_from_lifetime(frequency, acceptor_lifetime)
464
+ ),
465
+ )
466
+ )
467
+ self._on_changed(value)
468
+
469
+ def _on_changed(self, value: Any) -> None:
470
+ """Callback function to update plot with current slider values."""
471
+ frequency = self._frequency_slider.val
472
+ donor_lifetime = self._donor_lifetime_slider.val
473
+ acceptor_lifetime = self._acceptor_lifetime_slider.val
474
+ fret_efficiency = self._fret_efficiency_slider.val
475
+ donor_fretting = self._donor_fretting_slider.val
476
+ donor_bleedthrough = self._donor_bleedthrough_slider.val
477
+ acceptor_bleedthrough = self._acceptor_bleedthrough_slider.val
478
+ acceptor_background = self._acceptor_background_slider.val
479
+ donor_background = self._donor_background_slider.val
480
+ background_real = self._background_real_slider.val
481
+ background_imag = self._background_imag_slider.val
482
+ e = int(self._fret_efficiency_slider.val * 100)
483
+
484
+ donor_real, donor_imag = phasor_from_lifetime(
485
+ frequency, donor_lifetime
486
+ )
487
+ donor_fret_real, donor_fret_imag = phasor_from_lifetime(
488
+ frequency, donor_lifetime * (1.0 - fret_efficiency)
489
+ )
490
+ acceptor_real, acceptor_imag = phasor_from_lifetime(
491
+ frequency, acceptor_lifetime
492
+ )
493
+ donor_trajectory_real, donor_trajectory_imag = phasor_from_fret_donor(
494
+ frequency,
495
+ donor_lifetime,
496
+ fret_efficiency=self._fret_efficiencies,
497
+ donor_fretting=donor_fretting,
498
+ donor_background=donor_background,
499
+ background_real=background_real,
500
+ background_imag=background_imag,
501
+ )
502
+ (
503
+ acceptor_trajectory_real,
504
+ acceptor_trajectory_imag,
505
+ ) = phasor_from_fret_acceptor(
506
+ frequency,
507
+ donor_lifetime,
508
+ acceptor_lifetime,
509
+ fret_efficiency=self._fret_efficiencies,
510
+ donor_fretting=donor_fretting,
511
+ donor_bleedthrough=donor_bleedthrough,
512
+ acceptor_bleedthrough=acceptor_bleedthrough,
513
+ acceptor_background=acceptor_background,
514
+ background_real=background_real,
515
+ background_imag=background_imag,
516
+ )
517
+
518
+ if donor_background > 0.0:
519
+ self._donor_background_line.set_data(
520
+ [float(donor_real), float(background_real)],
521
+ [float(donor_imag), float(background_imag)],
522
+ )
523
+ else:
524
+ self._donor_background_line.set_data([0.0, 0.0], [0.0, 0.0])
525
+
526
+ if donor_fretting < 1.0 and donor_background == 0.0:
527
+ self._donor_donor_line.set_data(
528
+ [donor_real, donor_fret_real],
529
+ [donor_imag, donor_fret_imag],
530
+ )
531
+ else:
532
+ self._donor_donor_line.set_data([0.0, 0.0], [0.0, 0.0])
533
+
534
+ if acceptor_background > 0.0:
535
+ self._acceptor_background_line.set_data(
536
+ [float(acceptor_real), float(background_real)],
537
+ [float(acceptor_imag), float(background_imag)],
538
+ )
539
+ else:
540
+ self._acceptor_background_line.set_data([0.0, 0.0], [0.0, 0.0])
541
+
542
+ self._background_line.set_data([background_real], [background_imag])
543
+
544
+ self._donor_only_line.set_data([donor_real], [donor_imag])
545
+ self._donor_fret_line.set_data([donor_fret_real], [donor_fret_imag])
546
+ self._donor_trajectory_line.set_data(
547
+ donor_trajectory_real, donor_trajectory_imag
548
+ )
549
+ self._donor_line.set_data(
550
+ [donor_trajectory_real[e]], [donor_trajectory_imag[e]]
551
+ )
552
+
553
+ self._acceptor_only_line.set_data([acceptor_real], [acceptor_imag])
554
+ self._acceptor_trajectory_line.set_data(
555
+ acceptor_trajectory_real, acceptor_trajectory_imag
556
+ )
557
+ self._acceptor_line.set_data(
558
+ [acceptor_trajectory_real[e]], [acceptor_trajectory_imag[e]]
559
+ )