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