{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tutorial about fluopy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we outline the principle use of fluopy." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "from pprint import pprint\n", "\n", "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "import fluopy\n", "import fluopy.analysis as an\n", "import fluopy.blinking as bl\n", "import fluopy.emissions as em\n", "import fluopy.fcs as fcs_p\n", "import fluopy.figure as fi\n", "import fluopy.fluorophores as fl\n", "import fluopy.miscellaneous as mi\n", "import fluopy.prediction as pr\n", "import fluopy.simulation as si\n", "import fluopy.transitions as tr" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "fluopy.__version__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For random number generation a Generator is initialized." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "rng = np.random.default_rng(seed=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Modules" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fluopy contains the following modules. For detailed information look up the API documentation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Fluopy modules:\")\n", "print()\n", "pprint([item for item in dir(fluopy) if not item.startswith(\"_\")])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fluopy helps to run Markov chain simulations for a photophysical system of isolated or communicating fluorophores." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get started the photophysical system has to be defined and instantiated." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This consists of the following steps:\n", "\n", "* Define a fluorophore system that contains fluorophores at positions in space.\n", "* Define a set of possible transitions for the fluorophore system." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A simulation provides a series of transition events that occured in a single realization of the Markov chain.\n", "\n", "A simulation is run for a given fluorophore system and transition set with a random number generator initialized as seed." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For a fluorophore system and transition set that fullfill certain requirements a prediction for the simulation outcome can be derived based on computational estimation of a steady-state equilibrium." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, the acquired data is analysed and presented in various formats." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Analysis procedure that focus on experimentally observable transitions are available:\n", "\n", "* Time traces\n", "* Fluorescence correlation spectroscopy\n", "* ON/Off time distributions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A simulation with pulsed excitation can be carried out. The analysis resembles a TSCPC experiment." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A typical setup, simulation and analysis procedure for selected photophysical systems is presented in the other tutorials. \n", "\n", "In the following we will present the relevant code blocks." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Transitions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are various transition types that connect the photophysical states:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list(tr.TransitionType.__members__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Photophysical states are named according to the Enum class `tr.SingleState` and `tr.PairedState`, which link state IDs to photophysical state names.\n", "\n", "A single fluorophore can occupy the following photophysical states:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list(tr.SingleState.__members__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Two interacting fluorophores can occupy the following (paired) photophysical states:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list(tr.PairedState.__members__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A specific transition of a certain type from state 1 to state 2 is defined as:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transition = tr.Transition(\n", " transition_type=tr.TransitionType.EXCITATION, rate=1e6, fluorophore_ids=[1]\n", ")\n", "pprint(transition)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A photophysical system is made up of one or more fluorophores or fluorophore pairs with multiple transitions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transition_1 = tr.Transition(\n", " tr.TransitionType.EXCITATION, rate=1e6, fluorophore_ids=[0]\n", ")\n", "transition_2 = tr.Transition(\n", " tr.TransitionType.FLUORESCENT_EMISSION, rate=1e9, fluorophore_ids=[0]\n", ")\n", "transitions = {\n", " \"cy5_dna\": [transition_1, transition_2],\n", "}\n", "pprint(transitions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The fluorophore system" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A photophysical system is defined by one or more fluorophores that occupy electronic states and go through transitions from one state to another.\n", "\n", "A number of input parameters are kept in fluopy.fluo_data as FluorophoreData instances. These are automatically inserted if the name matches." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fluorophore = fl.Fluorophore(name=\"some_dye\", position=[0, 0])\n", "pprint(fluorophore)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fluorophore = fl.Fluorophore(name=\"cy5_dna\", position=[0, 0])\n", "pprint(fluorophore)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A fluophore system contains the fluorophore identities, their spatial arrangement and transition rate constants." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fluorophores = [fluorophore]\n", "fluorophore_system = fl.FluorophoreSystem(fluorophores=fluorophores)" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "fig, ax = plt.subplots(figsize=(10, 7))\n", "fluorophore_system.plot(scale=0.5, quadratic=True, ax=ax);" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fluorophore_system.plot(scale=1, quadratic=True);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fluorophore data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The transition rate constants are specific for a kind of fluorophore and the chemical environment. \n", "\n", "Standard datasets with all constant photophysical attributes of the fluorophore are defined for various fluorophores." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pprint(fluopy.fluo_data.atto643)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pprint(fluopy.fluo_data.cy5_dna)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The data is collected in a dataclass specifying possible constants `fluopy.fluo_data.FluorophoreData`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fluorophore_data = fluopy.fluo_data.FluorophoreData()\n", "pprint(fluorophore_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Transition set" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A set of possible transitions can be generated from a given fluorophore system by helper functions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transitions = fluorophore_system.load_transitions(\n", " summarize=False,\n", " irradiance=2.5,\n", " wavelength=640,\n", " bleaching=False,\n", " energy_transfer=False,\n", " dstorm=False,\n", ")\n", "pprint(transitions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A transition set has to be defined from the individual transitions for a given fluorophore system:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transition_set = tr.TransitionSet(transitions, fluorophore_system)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "TransitionSet provides helper methods so that a transition_set can be easily modified with the following methods:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transition_set_no_bleaching = transition_set.remove_absorbing_states()\n", "transition_set_no_ets = transition_set.remove_energy_transfers()\n", "transition_set_filtered = transition_set.filter_by_identity(remove_list=[6])\n", "\n", "transition_set_adj = transition_set.adjust_rates(\n", " change_dict={1: 1e3}, keep_zero_rates=False\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If for no other reasons you should remove transitions with rate constants of zero (this has possibly already been done above through keep_zero_rates=False):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transtition_set = transition_set.remove_zero_rates()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A transition set has to be finalized before a simulation is started:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transition_set.finalize()\n", "list(vars(transition_set).keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A graphical representation of all possible transitions is given:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transition_set.plot(graph_type=\"shell\", colors=None, scale=1);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All transitions are listed in the `transitions`attribute or in a dataframe:" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "for key, value in transition_set.transitions.items():\n", " pprint(key)\n", " pprint(value)\n", " print(\"---\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transition_set.transition_df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: In transition_set.single_states, only the 'relevant' states are mentioned - the ones that actually occur in transition_set.plot().\n", "\n", "Note: Transitions that have a rate of 0 or are not usable by the fluorophore do not occur transition_set.single_states; however, transitions that may not be reachable still do." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Single dye simulation setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For further demonstration we set up a simple fluorophore system that contains a single Cy5 fluorophore." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Define the fluorophore system" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fluorophore = fl.Fluorophore(name=\"cy5_dna\", position=[0, 0])\n", "fluorophore_system = fl.FluorophoreSystem(fluorophores=[fluorophore])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Define the transition set" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transitions = fluorophore_system.load_transitions(\n", " summarize=True,\n", " irradiance=2,\n", " wavelength=640,\n", " bleaching=False,\n", " energy_transfer=False,\n", " dstorm=False,\n", ")\n", "transition_set = tr.TransitionSet(transitions, fluorophore_system)\n", "transition_set = transition_set.remove_energy_transfers()\n", "transition_set.finalize()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Make a prediction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For a fluorophore system and transition set that fullfill certain requirements a prediction for the simulation outcome can be derived based on computational estimation of a steady-state equilibrium." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prediction = pr.Prediction(transition_set)\n", "list(vars(prediction).keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The results are accessible through methods of the prediction instance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prediction.plot_frequency_transitions(scale=0.5)\n", "prediction.plot_frequency_states(scale=0.5)\n", "prediction.plot_mean_lifetimes(scale=0.5)\n", "prediction.plot_mean_transition_times(scale=0.5)\n", "prediction.plot_state_occupations(scale=0.5)\n", "prediction.plot_transition_time_distributions(\n", " fluorophore=\"cy5_dna\", transition_id=0, scale=0.5\n", ");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note for multi-fluorophore systems:\n", "* if energy transfers occur, pairs have separate color in plot but are normalized with other transitions of their donor\n", "* absorbing states: the prediction works using the fundamental matrix. For Q, the transitions leading to an OVERALL absorbed state are dropped. This can be done since bleaching is usually by far the least occurring transition, meaning each fluorophore will reach its limiting distribution (as if it was no absorbing Markov Chain)\n", " the frequencies and state occupations of absorbing transitions and states are set to 0, their lifetimes to inf (this is different to analysis, where - if absorbing transitions happened - only the lifetime and the state occupation is 0)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Run a simulation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To carry out the complete Markov chain simulation, a Simulation object has to be instantiated from the transition set.\n", "\n", "The simulation can then run for a given number of transition steps, or until it reaches a defined end time." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "simulation = si.Simulation(transition_set)\n", "list(vars(simulation).keys())" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "%%time\n", "# basic simulation run for a given number of steps (size)\n", "simulation.run(start_at=None, size=1e6, end_time=None, seed=rng, use_memmap=None)\n", "mi.print_class(simulation)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "# simulate until it reaches given end_time\n", "simulation.run(start_at=None, size=1e6, end_time=1, seed=rng, use_memmap=None)\n", "mi.print_class(simulation)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the simulation goes through many transtition events, the arrays may become too large for the available RAM.\n", "In that case, use memory maps to store the data on the local drive." ] }, { "cell_type": "raw", "metadata": {}, "source": [ "simulation.run(start_at=None, size=1e6, end_time=None, seed=rng,\n", " use_memmap='')\n", "mi.print_class(simulation)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Run an approximate simulation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Under certain requirements (similar to prediction) a simulation approximation can can be carried out, saving time." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "approximation = si.Simulation(transition_set=transition_set)\n", "approximation.approximate(prediction=prediction, size=1e6, seed=rng)\n", "list(vars(approximation).keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Analyze the simulation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Analyze the data from a given simulation for state probabilities and lifetimes, among others." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "analysis = an.Analysis(simulation=simulation)\n", "list(vars(analysis).keys())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mi.print_class(analysis)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the following analysis plots the simulation results are overlaid with prediction outcomes (crosses) if a prediction is provided." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "analysis.get_fluorescence_lifetimes(fluorophore=\"cy5_dna\")\n", "analysis.get_emitting_transition_lifetimes(fluorophore=\"cy5_dna\")\n", "\n", "analysis.plot_frequency_transitions(scale=0.5, prediction=prediction)\n", "analysis.plot_frequency_states(scale=0.5, prediction=prediction)\n", "analysis.plot_mean_transition_times(scale=0.5, prediction=prediction)\n", "analysis.plot_mean_lifetimes(scale=0.5, prediction=prediction)\n", "analysis.plot_state_occupations(scale=0.5, prediction=prediction)\n", "analysis.plot_lifetime_distributions(\n", " scale=0.5, prediction=prediction, fluorophore=\"cy5_dna\", state_identity=1\n", ")\n", "analysis.plot_transition_time_distributions(\n", " scale=0.5, prediction=prediction, fluorophore=\"cy5_dna\", transition_id=0\n", ");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note for multi-fluorophore systems:\n", "\n", "* The analysis follows these rules:\n", "\n", " * time to transitions of energy transfers are collected only from the donor's point of view.\n", "\n", " * time to transition of e.g., fluorescence, does not differentiate whether energy transfer was also an option or not one energy transfer transition occurrence resembles the transition of the donor and the acceptor state lifetimes and occurrences do not differentiate whether energy transfer was involved or not.\n", "\n", "* Energy transfer has an impact on true fluorescence lifetime only if the acceptors are truly available. If the potential acceptor is in a non-accepting state, the energy transfer is not available. If multiple fluorophores are potential acceptors, all of their respective rates are taken into account. Depending on the photophysical system, energy transfers may only have a marginal impact on the mean fluorescence lifetime.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulation of experimentally observable (photons per frames) only" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you are not interested in all hidden state transitions but only in the observable photon emission events, you can limit the simulation to the relevant transitions.\n", "\n", "This approach requires less memory than a full simulation. \n", "\n", "However, the complete statistical analysis is not available. A photon time series, FCS and blinking analysis is available.\n", "\n", "The observation of emitted photons is further influenced by spectral filters, optical elements and detector noise contributions.\n", "\n", "For multicolor fluorophore-systems, instantiate two Emissions objects with different bandpasses." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Extract photon emission events from simulation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "emissions = em.Emissions(frame_time=\"5ms\", seed=rng, bandpass=(600, 800))\n", "emissions.extract(simulation=simulation)\n", "emissions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Simulate photon emission events" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "%%time\n", "emissions = em.Emissions(frame_time=\"5ms\", seed=rng, bandpass=(600, 800))\n", "emissions.simulate(transition_set=transition_set, store_time_points=True, frames=2000)\n", "list(vars(emissions).keys())" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "vars(emissions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Helper routines are available to further modify the emission time series and correct for detection efficiency and noise contributions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "emissions.add_photon_collection_objective(p=0.1, seed=rng) # 1.\n", "emissions.add_quantum_efficiency(p=0.9, seed=rng) # 3.1.\n", "emissions.add_poisson_noise(\n", " rate=0.05, seed=rng\n", ") # 3.2. (dark noise), note the frame time\n", "emissions.add_emccd_gain(emccd_gain=10, seed=rng) # 4.\n", "emissions.add_gaussian_noise(mean=10, std=1, seed=rng) # 5. (readout noise)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "emissions.plot_time_series();" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "emissions.plot_histogram();" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "emissions.plot_cumulative_events();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulation of pulsed excitation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A simulation of observable photon emissions with pulsed laser excitation can be carried out. The analysis resembles a TSCPC experiment.\n", "\n", "In this simulation, the returned lifetimes are the time differences of photon emission to the last laser pulse. I.e., if the time between laser pulses is very short, it may shorten the observed lifetimes. \n", "\n", "The simulation does not discriminate between multiple identical fluorophores (i.e., if one fluorophore gets excited, it transfers the energy to another fluorophore, and this fluorophore emits a photon). \n", "\n", "However, if the fluorophores are different, only one of them may be directly excitable by the laser pulse (due to the wavelength) and, depending on the provided bandpass, only the fluorescence of the other may be detected.\n", "\n", "If details is True, a complete simulation object will be returned with transition_series, state_series and time_series (requiring more memory). " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "emissions_tcspc = em.Emissions(frame_time=\"10us\", seed=rng, bandpass=(600, 800))\n", "lifetimes_DA, lifetimes_D, lifetimes_all, simulation_object = emissions_tcspc.tcspc(\n", " transition_set=transition_set,\n", " number_pulses=1e5,\n", " pulse_duration=1e-11,\n", " time_between_pulses=1e-7,\n", " excitation_rates={\"cy5_dna\": 1e11},\n", " size=1e5,\n", " store_time_points=True,\n", " # details = True\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "emissions_tcspc.plot_time_series()\n", "fi.universal_figure(\n", " type_=\"hist\", data=lifetimes_all, ylabel=\"PD\", density=True, xlabel=\"Lifetime (s)\"\n", ");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note:\n", "\n", "Since the TCSPC simulation assumes a laser pulse to be instantaneous, a laser pulse that excited multiple fluorophores will lead to duplicates in the time series. This is handled by introducing minimal amount of spaces between these duplicates. The excitations are added in the correct order (fluorophore-specific) such that these series exactly represent the transition chain, even with multiple different distances between energy transfer partners. Note that when monitoring the fluorescence lifetime via the simulation object it will be the true one (as opposed to the generally returned lifetimes, which are the observed ones)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fluorescence correlation spectroscopy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Observed fluorescence emission events can be analyzed by a correlation analysis." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fcs = fcs_p.FCS(emissions)\n", "list(vars(fcs).keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Autocorrelation of time points" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The autocorrelation can be computed on the exact time points (being limited by the smallest available time difference)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "fcs.autocorrelate_time_points(\n", " exp_min=-16, exp_max=0, points_per_base=4, base=4, normalize=True\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mi.print_class(fcs)\n", "fcs.plot(normalize_to=None, unit=\"s\", scale=1);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Autocorrelation of time series" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The autocorrelation can be computed on the binned time series (being limited by the time bin).\n", "\n", "There is not much to be seen in this example, since the bin time is larger than all relevant fluctuation time scales." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fcs.autocorrelate_time_series(log=True, m=4, normalize=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mi.print_class(fcs)\n", "fcs.plot(normalize_to=None, unit=\"s\", scale=1);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some fcs fits are available:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# fcs_predict = fcs_p.fit_dark(tau, dark_lifetime, dark_occupation)\n", "# fcs_predict = fcs_p.fit_antibunching(tau, excitation_rate, s1_lifetime)\n", "# fcs_predict = fcs_p.fit_triplet_cis(tau, k_isc, k_T, k_01, k_10, k_iso, k_biso_eff)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Antibunching" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, you can focus on fast time scales in a linear scale and observe photon bunching or antibunching." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# sensible to tau_max and bin_width, see coincidence notebook\n", "hist, bins = fcs_p.coincidence(\n", " emissions.event_time_points[: int(2e5)], tau_max=1e-7, bin_width=1e-9, seed=rng\n", ")\n", "fi.universal_figure(\n", " type_=\"line\",\n", " data=[bins, hist],\n", " xlabel=r\"$\\tau$ (s)\",\n", " ylabel=r\"$g^{(2)}(\\tau)$\",\n", " scale=1,\n", ");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Blinking" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For analyzing a fluorescence on/off process, the blinking time scales can be estimated by thresholding an emission time series.\n", "\n", "An ON-period is a number of consecutive frames where each frame contains a minimum amount of emissions (> threshold). \n", "\n", "An OFF-period is a number of consecutive frames where each frame contains a maximum amount of emissions (≤ threshold). \n", "\n", "Each ON-period is followed by an OFF-period and vice versa." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Emissions from a short simulation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We limit the dataset to 2000 frames for illustration purposes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "emissions = em.Emissions(frame_time=\"10us\", seed=rng, bandpass=None)\n", "emissions.simulate(transition_set=transition_set, store_time_points=False, frames=2000)\n", "emissions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "threshold: int = 5" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "emissions.plot_time_series(scale=1)\n", "plt.hlines(threshold, xmin=0, xmax=0.02)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "blinks = bl.Blinking(emissions, threshold=threshold)\n", "blinks" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mi.print_class(blinks)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# plot a histogram of OFF times\n", "blinks.plot(\n", " mode=\"off_histogram\", density=True, display_mean=True, as_time=\"ms\", scale=0.5\n", ")\n", "\n", "# plot a histogram of ON times\n", "blinks.plot(\n", " mode=\"on_histogram\", density=True, display_mean=True, as_time=\"ms\", scale=0.5\n", ")\n", "\n", "# plot a time series of OFF times\n", "blinks.plot(mode=\"off_frame_series\", scale=0.5)\n", "\n", "# plot a time series of ON times\n", "blinks.plot(mode=\"on_frame_series\", scale=0.5);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can extract the analytical OFF statistics from the emission time series without differentiating between fluorophores." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "on_off_times_analytic, on_off_values_analytic = bl.get_analytical_off_statistics(\n", " off_frames=blinks.off_periods_frames,\n", " off_periods=blinks.off_periods,\n", " on_frames=blinks.on_periods_frames,\n", " frame_time=blinks.emissions.parameters[\"frame_time\"],\n", ")\n", "\n", "bl.plot_off_statistics(\n", " on_off_times_analytic, on_off_values_analytic, scale=1, title=\"analytical OFF\"\n", ");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Emissions from the long simulation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Get more detailed information from a complete simulation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "emissions = em.Emissions(frame_time=\"200ns\", seed=rng, bandpass=None)\n", "emissions.extract(simulation=simulation)\n", "emissions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "blinks = bl.Blinking(emissions, threshold=threshold)\n", "blinks" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mi.print_class(blinks)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# plot a histogram of OFF times\n", "blinks.plot(\n", " mode=\"off_histogram\", density=True, display_mean=True, as_time=\"ms\", scale=0.5\n", ")\n", "\n", "# plot a histogram of ON times\n", "blinks.plot(\n", " mode=\"on_histogram\", density=True, display_mean=True, as_time=\"ms\", scale=0.5\n", ")\n", "\n", "# plot a time series of OFF times\n", "blinks.plot(mode=\"off_frame_series\", scale=0.5)\n", "\n", "# plot a time series of ON times\n", "blinks.plot(mode=\"on_frame_series\", scale=0.5)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.4" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }