chemistrykit.photochem#
chemistrykit.photochem: photochemistry.
Jablonski-diagram excited-state kinetics (fluorescence, internal
conversion, intersystem crossing, and phosphorescence) modeled as a
mass-action reaction network and integrated via
chemistrykit.kinetics/chemistrykit.integrators;
fluorescence/phosphorescence quantum yields and the photochemical
quantum yield via Beer-Lambert (chemistrykit.spectro);
Stern-Volmer fluorescence quenching with a static-vs-dynamic diagnostic;
photostationary-state kinetics for a two-state photoswitch under
simultaneous forward/reverse photolysis; the H2/Cl2 photochemical chain
reaction; Stokes shifts and Perrin fluorescence anisotropy; Kasha’s-rule
emission yields; Förster and Dexter energy transfer; ferrioxalate
actinometry; and Rehm-Weller electron-transfer quenching.
- class chemistrykit.photochem.PhotostationaryStateResult(A_pss, B_pss, ratio_B_over_A, k_AB, k_BA)[source]#
Bases:
objectResult of a
chemistrykit.photochem.systems.photostationary_state.photostationary_state()call.
- class chemistrykit.photochem.SternVolmerFit(Ksv, r_squared)[source]#
Bases:
objectResult of fitting intensity-ratio-vs-quencher-concentration data to the Stern-Volmer equation.
- chemistrykit.photochem.chain_quantum_yield(k2, kt, H2, I_abs)[source]#
Steady-state quantum yield of the H2/Cl2 chain: \(\Phi = 2k_2[\mathrm{H_2}]/\sqrt{k_t I_{abs}}\).
- Parameters:
- Returns:
float or ndarray – HCl molecules formed per photon absorbed – not bounded by 1.
Examples
Halving the light intensity by a factor of 4 doubles the quantum yield (the chain lengthens as \(I_{abs}^{-1/2}\)):
>>> phi1 = chain_quantum_yield(k2=10.0, kt=100.0, H2=1.0, I_abs=1e-4) >>> phi2 = chain_quantum_yield(k2=10.0, kt=100.0, H2=1.0, I_abs=0.25e-4) >>> round(phi1, 6), round(phi2 / phi1, 6) (200.0, 2.0)
- chemistrykit.photochem.classify_quenching_mechanism(intensity_ratio_slope, lifetime_ratio_slope, rtol=0.1)[source]#
Classify a quenching mechanism as dynamic, static, or mixed from the two Stern-Volmer slopes.
Measuring both the steady-state intensity ratio’s Stern-Volmer slope and the excited-state lifetime ratio’s is the standard diagnostic (Lakowicz, Principles of Fluorescence Spectroscopy, 3rd ed., Ch. 8.2): pure dynamic (collisional) quenching reduces the lifetime and the intensity by the same factor, so the two slopes agree (\((I_0/I)\) and \((\tau_0/\tau)\) are superimposable); pure static quenching (ground-state complexation) removes a fraction of fluorophores from the population entirely without shortening the lifetime of the ones that remain uncomplexed, so \(\tau_0/\tau=1\) (slope 0) while \(I_0/I\) still rises linearly. A slope ratio between these extremes indicates both mechanisms are contributing.
- Parameters:
- Return type:
- Returns:
str – One of
"dynamic","static", or"mixed".
Examples
Equal slopes indicate purely dynamic quenching:
>>> classify_quenching_mechanism(intensity_ratio_slope=40.0, lifetime_ratio_slope=40.0) 'dynamic'
A vanishing lifetime slope with a nonzero intensity slope indicates purely static quenching:
>>> classify_quenching_mechanism(intensity_ratio_slope=40.0, lifetime_ratio_slope=0.0) 'static'
Something in between indicates a mixed mechanism:
>>> classify_quenching_mechanism(intensity_ratio_slope=40.0, lifetime_ratio_slope=20.0) 'mixed'
- chemistrykit.photochem.dexter_rate(r, K, J, L)[source]#
Dexter exchange energy-transfer rate \(k_{ET} = KJ\exp(-2r/L)\).
Exchange transfer requires the donor’s and acceptor’s electron clouds to overlap, so the rate decays exponentially with separation rather than as the \(r^{-6}\) power law of Förster transfer, and it allows spin-forbidden triplet-triplet transfer that dipole coupling does not (Dexter 1953; Turro et al., Ch. 7).
- Parameters:
- Returns:
float or ndarray
Examples
Each increase of \(r\) by \(L/2\) reduces the rate by a factor of \(e\):
>>> k1, k2 = dexter_rate(1.0, K=1.0, J=1.0, L=2.0), dexter_rate(2.0, K=1.0, J=1.0, L=2.0) >>> round(k1 / k2, 6) == round(float(np.e), 6) True
- chemistrykit.photochem.dynamic_quenching_constant(kq, tau0)[source]#
The Stern-Volmer constant for purely dynamic (collisional) quenching: \(K_{sv} = k_q\tau_0\).
\(k_q\) is the bimolecular quenching rate constant (an encounter/collision frequency, at most diffusion-controlled) and \(\tau_0\) the unquenched excited-state lifetime – a longer- lived excited state has more opportunity to be collisionally quenched before it decays on its own, hence the product form (Lakowicz, Principles of Fluorescence Spectroscopy, 3rd ed., Ch. 8, eq. 8.5).
- Parameters:
- Return type:
- Returns:
float – \(K_{sv}\), in 1/M.
Examples
>>> round(dynamic_quenching_constant(kq=1.0e10, tau0=5.0e-9), 4) 50.0
- chemistrykit.photochem.ferrioxalate_fe2_moles(absorbance_510, volume_L, path_length_cm=1.0, epsilon_510=11100.0)[source]#
Moles of Fe(II) from the absorbance of its phenanthroline complex at 510 nm.
Beer-Lambert: \(n = A_{510} V/(\varepsilon_{510}\ell)\).
- Parameters:
- Returns:
float or ndarray – Moles of Fe(II).
Examples
>>> round(ferrioxalate_fe2_moles(1.11, volume_L=0.01) * 1e6, 6) 1.0
- chemistrykit.photochem.ferrioxalate_photon_flux(moles_fe2, time_s, absorbance=inf, quantum_yield=1.21)[source]#
Incident photon flux \(q_p = n_{Fe^{2+}}/(\Phi\,t\,(1-10^{-A}))\) from ferrioxalate actinometry.
- Parameters:
moles_fe2 (float or array-like of float) – Moles of Fe(II) formed (e.g. from
ferrioxalate_fe2_moles()).time_s (float or array-like of float) – Irradiation time, in s.
absorbance (
float) – Actinometer absorbance at the irradiation wavelength (infmeans total absorption, the usual operating regime).quantum_yield (
float) – Fe(II) quantum yield at the irradiation wavelength (1.21 near 365 nm per Hatchard & Parker).
- Returns:
float or ndarray – Incident photon flux, in einstein/s (mol photons/s).
Examples
1.21 micromol Fe(II) in 100 s under total absorption means \(10^{-8}\) einstein/s:
>>> round(ferrioxalate_photon_flux(1.21e-6, 100.0) * 1e8, 6) 1.0
- chemistrykit.photochem.fit_stern_volmer(Q, intensity_ratio)[source]#
Fit \(I_0/I\) (or \(\tau_0/\tau\)) vs. \([Q]\) data to the Stern-Volmer equation.
Fits \(I_0/I = 1 + K_{sv}[Q]\) by ordinary least squares, constraining the intercept to 1 exactly (the physically required value at \([Q]=0\)) by fitting the shift \(I_0/I - 1\) through the origin against Q – structurally similar to
chemistrykit.kinetics.systems.arrhenius.fit_arrhenius()’s linearized fit, but with a fixed rather than free intercept.- Parameters:
- Return type:
- Returns:
SternVolmerFit
Examples
Generate exact data from a known \(K_{sv}\) and recover it:
>>> Q = np.array([0.0, 0.005, 0.010, 0.020, 0.040]) >>> ratio = stern_volmer_ratio(Ksv=35.0, Q=Q) >>> fit = fit_stern_volmer(Q, ratio) >>> round(fit.Ksv, 6) 35.0 >>> round(fit.r_squared, 6) 1.0
- chemistrykit.photochem.fluorescence_quantum_yield(kf, kic, kisc)[source]#
Fluorescence quantum yield \(\Phi_f = k_f/(k_f+k_{ic}+k_{isc})\).
The fraction of excited \(S_1\) molecules that relax by emitting a photon (fluorescence) rather than through either nonradiative decay channel – the ratio of the radiative rate constant to the total \(S_1\) decay rate (Lakowicz, Principles of Fluorescence Spectroscopy, 3rd ed., Ch. 1, eq. 1.1). See
chemistrykit.photochem.systems.jablonskifor the underlying kinetic model this ratio comes from.- Parameters:
- Return type:
- Returns:
float – In \([0, 1]\).
Examples
With no competing nonradiative pathway, every excited molecule fluoresces:
>>> round(fluorescence_quantum_yield(kf=1.0, kic=0.0, kisc=0.0), 6) 1.0
Adding a nonradiative channel of the same magnitude as kf exactly halves the yield:
>>> round(fluorescence_quantum_yield(kf=1.0, kic=1.0, kisc=0.0), 6) 0.5
- chemistrykit.photochem.forster_efficiency(r, R0)[source]#
FRET efficiency \(E = 1/(1+(r/R_0)^6)\).
- Parameters:
- Returns:
float or ndarray – Efficiency in \([0,1]\).
Examples
>>> forster_efficiency(5.0, R0=5.0) 0.5 >>> round(forster_efficiency(10.0, R0=5.0), 5) 0.01538
- chemistrykit.photochem.forster_radius(kappa2, n, quantum_yield_donor, overlap_J)[source]#
Förster distance \(R_0 = 0.211\,(\kappa^2 n^{-4} Q_D J)^{1/6}\) Å.
The donor-acceptor distance at which transfer is 50% efficient (Lakowicz, 3rd ed., eq. 13.12), with the spectral overlap integral \(J\) in M-1 cm-1 nm4.
- Parameters:
- Return type:
- Returns:
float – \(R_0\), in Å.
Examples
A typical dye pair (\(J=10^{15}\), \(Q_D=0.5\), \(n=1.4\)) gives \(R_0\) of about 4.4 nm:
>>> round(forster_radius(2 / 3, 1.4, 0.5, 1.0e15), 1) 44.4
- chemistrykit.photochem.forster_rate(r, R0, tau_D)[source]#
Förster transfer rate \(k_T = \tau_D^{-1}(R_0/r)^6\).
- Parameters:
- Returns:
float or ndarray – Transfer rate constant, in 1/(unit of tau_D).
Examples
At \(r=R_0\) transfer competes equally with donor decay:
>>> forster_rate(5.0, R0=5.0, tau_D=2.0) 0.5
- chemistrykit.photochem.hydrogen_chlorine_chain_network(j, k2, k3, kt, H2_0=1.0, Cl2_0=1.0)[source]#
Build Nernst’s photochemical H2/Cl2 atom-chain mechanism as a mass-action network.
- Parameters:
j (
float) – Photolysis rate constant of \(\mathrm{Cl_2}\) (so the absorbed photon rate per volume is \(I_{abs}=j[\mathrm{Cl_2}]\), assuming each absorbed photon dissociates one \(\mathrm{Cl_2}\)).k2 (
float) – \(\mathrm{Cl}+\mathrm{H_2}\to\mathrm{HCl}+\mathrm{H}\) rate constant.k3 (
float) – \(\mathrm{H}+\mathrm{Cl_2}\to\mathrm{HCl}+\mathrm{Cl}\) rate constant.kt (
float) – \(\mathrm{Cl}+\mathrm{Cl}\to\mathrm{Cl_2}\) termination rate constant (mass-action rate \(k_t[\mathrm{Cl}]^2\)).H2_0 (
float) – Initial concentrations.Cl2_0 (
float) – Initial concentrations.
- Return type:
- Returns:
StoichiometricNetwork – Species
("H2", "Cl2", "Cl", "H", "HCl").
Examples
Hydrogen atoms are conserved between H2, H and HCl:
>>> net = hydrogen_chlorine_chain_network(j=1e-4, k2=10.0, k3=100.0, kt=100.0) >>> res = net.integrate((0.0, 5.0), dt=1e-3, method="rk4") >>> H_total = 2 * res.concentration("H2") + res.concentration("H") + res.concentration("HCl") >>> bool(np.allclose(H_total, 2.0)) True
- chemistrykit.photochem.intersystem_crossing_yield(kisc, kf, kic)[source]#
Intersystem-crossing yield \(\Phi_{isc} = k_{isc}/(k_f+k_{ic}+k_{isc})\).
The fraction of excited \(S_1\) population that crosses to the triplet manifold \(T_1\) rather than returning directly to \(S_0\) (Turro, Ramamurthy & Scaiano, Modern Molecular Photochemistry of Organic Molecules, Ch. 5).
- Parameters:
- Return type:
- Returns:
float – In \([0, 1]\).
Examples
Together with
fluorescence_quantum_yield()and the (implicit) internal-conversion yield, the three \(S_1\) decay-channel yields sum to exactly 1:>>> kf, kic, kisc = 2.0, 1.0, 0.5 >>> phi_f = fluorescence_quantum_yield(kf, kic, kisc) >>> phi_isc = intersystem_crossing_yield(kisc, kf, kic) >>> phi_ic = kic / (kf + kic + kisc) >>> round(phi_f + phi_isc + phi_ic, 9) 1.0
- chemistrykit.photochem.jablonski_network(kf, kic, kisc, kp, kic_T, S1_0=1.0)[source]#
Build the 3-state (\(S_1,T_1,S_0\)) Jablonski excited-state decay network.
Structurally, \(S_1\) decays through two parallel first-order channels (directly to \(S_0\) at combined rate \(k_f+k_{ic}\), and to \(T_1\) at rate \(k_{isc}\)), and \(T_1\) then decays to \(S_0\) at combined rate \(k_p+k_{ic,T}\) – a branching-then-consecutive network, built here as a general
StoichiometricNetwork(its three named constructors,parallel/consecutive/reversible, don’t individually cover this branching topology, but the general mass-action constructor they’re built on does).- Parameters:
kf (
float) – Fluorescence (\(S_1\to S_0\), radiative) rate constant.kic (
float) – Internal conversion (\(S_1\to S_0\), nonradiative) rate constant.kisc (
float) – Intersystem crossing (\(S_1\to T_1\)) rate constant.kp (
float) – Phosphorescence (\(T_1\to S_0\), radiative) rate constant.kic_T (
float) – Triplet nonradiative decay (\(T_1\to S_0\)) rate constant.S1_0 (
float) – Initial \(S_1\) population immediately after excitation (e.g. following a short excitation pulse).
- Return type:
- Returns:
StoichiometricNetwork – Species
("S1", "T1", "S0").
Examples
Total population is conserved (S0 is a pure sink, nothing is created or destroyed, just redistributed):
>>> net = jablonski_network(kf=2.0, kic=1.0, kisc=0.5, kp=0.3, kic_T=0.2, S1_0=1.0) >>> result = net.integrate((0.0, 20.0), dt=1e-3, method="rk4") >>> total = result.concentration("S1") + result.concentration("T1") + result.concentration("S0") >>> bool(np.allclose(total, 1.0, atol=1e-6)) True
- chemistrykit.photochem.jablonski_populations_analytic(kf, kic, kisc, kp, kic_T, S1_0, t)[source]#
Closed-form \(S_1(t)\), \(T_1(t)\), \(S_0(t)\) for the 3-state Jablonski network.
\(S_1\) decays as a simple single exponential with total rate \(k_{S_1}=k_f+k_{ic}+k_{isc}\):
\[[S_1](t) = [S_1]_0 e^{-k_{S_1}t}\]\(T_1\) is fed by the fraction \(k_{isc}/k_{S_1}\) of decayed \(S_1\) and itself decays at total rate \(k_{T_1}=k_p+k_{ic,T}\); solving \(d[T_1]/dt=k_{isc}[S_1]-k_{T_1}[T_1]\) gives a scaled version of the same Bateman-equation form as a consecutive \(A\to B\to C\) chain (Turro, Ramamurthy & Scaiano, Modern Molecular Photochemistry of Organic Molecules, Ch. 5; the underlying algebra is identical to
chemistrykit.kinetics.systems.networks.consecutive_analytic(), which this function calls directly and rescales by \(k_{isc}/k_{S_1}\) – since only that fraction of decayed \(S_1\), not all of it, feeds \(T_1\)):\[[T_1](t) = \frac{k_{isc}}{k_{S_1}} \left[\frac{[S_1]_0 k_{S_1}}{k_{T_1}-k_{S_1}} \left(e^{-k_{S_1}t}-e^{-k_{T_1}t}\right)\right]\]and \([S_0](t) = [S_1]_0 - [S_1](t) - [T_1](t)\) by mass balance. This closed form was checked against direct numerical integration of
jablonski_network()(to a relative agreement of better than \(10^{-10}\) across a range of rate constants) as part of developing this module.- Parameters:
kf (
float) – Rate constants, as injablonski_network().kic (
float) – Rate constants, as injablonski_network().kisc (
float) – Rate constants, as injablonski_network().kp (
float) – Rate constants, as injablonski_network().kic_T (
float) – Rate constants, as injablonski_network().S1_0 (
float) – Initial \(S_1\) population.t (float or array-like of float) – Time(s) at which to evaluate the populations.
- Returns:
S1, T1, S0 (float or ndarray)
Examples
Matches
jablonski_network()’s numerical integration:>>> kf, kic, kisc, kp, kic_T, S1_0 = 2.0, 1.0, 0.5, 0.3, 0.2, 1.0 >>> net = jablonski_network(kf, kic, kisc, kp, kic_T, S1_0) >>> result = net.integrate((0.0, 10.0), dt=1e-3, method="rk4") >>> S1, T1, S0 = jablonski_populations_analytic(kf, kic, kisc, kp, kic_T, S1_0, result.t) >>> bool(np.allclose(result.concentration("S1"), S1, atol=1e-6)) True >>> bool(np.allclose(result.concentration("T1"), T1, atol=1e-6)) True
- chemistrykit.photochem.kasha_emission_yields(kf2, k_ic21, kf1, knr1, excite='S2')[source]#
Emission quantum yields from \(S_2\) and \(S_1\) for a four-level (\(S_2,S_1,S_0\)) model.
After excitation into \(S_2\), the molecule either emits directly from \(S_2\) (rate \(k_{f2}\)) or internally converts to \(S_1\) (rate \(k_{ic,21}\)); from \(S_1\) it fluoresces (\(k_{f1}\)) or decays nonradiatively by any other channel (\(k_{nr1}\), lumping \(k_{ic}+k_{isc}\)). Branching ratios give
\[\Phi_{S_2} = \frac{k_{f2}}{k_{f2}+k_{ic,21}}, \qquad \Phi_{S_1} = \frac{k_{ic,21}}{k_{f2}+k_{ic,21}}\cdot \frac{k_{f1}}{k_{f1}+k_{nr1}}.\]Because \(k_{ic,21}\) (typically \(10^{12}\)-\(10^{14}\) s-1) vastly exceeds \(k_{f2}\) (\(\sim10^8\)-\(10^9\) s-1), \(\Phi_{S_2}\approx0\) and essentially all emission comes from \(S_1\) – Kasha’s rule (M. Kasha, Discuss. Faraday Soc. 9, 14 (1950); Turro, Ramamurthy & Scaiano, Modern Molecular Photochemistry of Organic Molecules, Ch. 4). Excitation directly into \(S_1\) (
excite="S1") gives \(\Phi_{S_2}=0\) and \(\Phi_{S_1}=k_{f1}/(k_{f1}+k_{nr1})\).- Parameters:
- Returns:
phi_S2, phi_S1 (float) – Quantum yields of emission from \(S_2\) and from \(S_1\).
Examples
With realistic rates almost no emission comes from \(S_2\), and the \(S_1\) fluorescence yield barely depends on which state was excited:
>>> phi2, phi1 = kasha_emission_yields(kf2=1e8, k_ic21=1e13, kf1=1e8, knr1=1e8) >>> phi2 < 1e-4, round(phi1, 4) (True, 0.5) >>> kasha_emission_yields(kf2=1e8, k_ic21=1e13, kf1=1e8, knr1=1e8, excite="S1") (0.0, 0.5)
- chemistrykit.photochem.perrin_anisotropy(r0, tau, theta)[source]#
Perrin equation for steady-state fluorescence anisotropy, \(r = r_0/(1+\tau/\theta)\).
A fluorophore excited by polarized light emits polarized light, but rotational diffusion during the excited-state lifetime \(\tau\) scrambles the orientation; for a spherical rotor with rotational correlation time \(\theta\) the time-averaged anisotropy is \(r_0/r = 1+\tau/\theta\) (Perrin 1926; Lakowicz, Ch. 10, eq. 10.45). \(r_0\) (at most 0.4 for one-photon excitation) is the anisotropy in the absence of rotation.
- Parameters:
- Returns:
float or ndarray
Examples
When the lifetime equals the correlation time, the anisotropy is halved:
>>> round(perrin_anisotropy(0.4, tau=4.0, theta=4.0), 6) 0.2
- chemistrykit.photochem.phosphorescence_quantum_yield(kisc, kf, kic, kp, kic_T)[source]#
Phosphorescence quantum yield \(\Phi_p = \Phi_{isc}\cdot\frac{k_p}{k_p+k_{ic,T}}\).
Phosphorescence requires two successive branching events to succeed: the molecule must first cross to the triplet manifold (probability \(\Phi_{isc}\)), and the resulting triplet must then decay radiatively rather than nonradiatively (probability \(k_p/(k_p+k_{ic,T})\)) – the product of these two independent branching ratios (Lakowicz, Principles of Fluorescence Spectroscopy, 3rd ed., Ch. 1; Turro, Ramamurthy & Scaiano, Modern Molecular Photochemistry of Organic Molecules, Ch. 5).
- Parameters:
- Return type:
- Returns:
float – In \([0, 1]\).
Examples
With no intersystem crossing at all, no phosphorescence is possible:
>>> round(phosphorescence_quantum_yield(kisc=0.0, kf=1.0, kic=0.0, kp=1.0, kic_T=0.0), 6) 0.0
With every \(S_1\) crossing to the triplet, and every triplet decaying radiatively, the phosphorescence yield is exactly 1:
>>> round(phosphorescence_quantum_yield(kisc=1.0, kf=0.0, kic=0.0, kp=1.0, kic_T=0.0), 6) 1.0
- chemistrykit.photochem.photochemical_quantum_yield(moles_product_formed, moles_photons_absorbed)[source]#
Photochemical quantum yield \(\Phi = \frac{\text{moles of product formed}}{\text{moles of photons absorbed}}\).
The photochemistry analogue of the excited-state quantum yields above: the efficiency of converting absorbed photons into chemical product (Turro, Ramamurthy & Scaiano, Modern Molecular Photochemistry of Organic Molecules, Ch. 7). Note on range: for a simple, non-chain photoreaction \(0\le\Phi\le1\) (each absorbed photon produces at most one product molecule), but a radical-chain photoreaction can propagate after the initiating absorption event and give \(\Phi\gg1\) – this function does not itself enforce an upper bound, since chain reactions are a real and common exception, not implemented here.
- Parameters:
- Returns:
float or ndarray
Examples
One photon absorbed producing exactly one product molecule (a typical simple photoisomerization) gives \(\Phi=1\):
>>> round(float(photochemical_quantum_yield(1.0, 1.0)), 6) 1.0
- chemistrykit.photochem.photons_absorbed(photon_flux_incident, absorbance)[source]#
Photon flux (or count) actually absorbed by a sample, from the Beer-Lambert absorbance.
\(I_{abs} = I_0(1-10^{-A}) = I_0(1-T)\), using
chemistrykit.spectro.systems.beer_lambert.transmittance()for \(T=10^{-A}\) – the fraction of incident light not transmitted is, by energy conservation (neglecting reflection and scattering losses), the fraction absorbed (Turro, Ramamurthy & Scaiano, Modern Molecular Photochemistry of Organic Molecules, Ch. 7).- Parameters:
photon_flux_incident (float or array-like of float) – Incident photon flux (or photon count over an exposure time), in any consistent unit (e.g. mol photons / s, or einstein/s).
absorbance (float or array-like of float) – Beer-Lambert absorbance of the sample at the excitation wavelength (see
chemistrykit.spectro.systems.beer_lambert).
- Returns:
float or ndarray – Absorbed photon flux, same unit as photon_flux_incident.
Examples
At zero absorbance, nothing is absorbed:
>>> round(float(photons_absorbed(1.0, absorbance=0.0)), 6) 0.0
At very high absorbance, essentially all incident light is absorbed:
>>> round(float(photons_absorbed(1.0, absorbance=10.0)), 6) 1.0
- chemistrykit.photochem.photostationary_ratio(k_AB, k_BA)[source]#
The photostationary-state population ratio \([B]_{pss}/[A]_{pss} = k_{AB}/k_{BA}\).
At the photostationary state, \(d[A]/dt=-k_{AB}[A]+k_{BA}[B]=0\), giving this ratio directly (E. Fischer, J. Phys. Chem. 71, 3704 (1967); Turro, Ramamurthy & Scaiano, Modern Molecular Photochemistry of Organic Molecules, Ch. 7.6) – exactly the long-time limit of
chemistrykit.kinetics.systems.networks.reversible_analytic()’s equilibrium-population formula, since a photostationary state is mathematically the same steady state a thermally reversible first-order reaction relaxes to.- Parameters:
- Return type:
- Returns:
float
Examples
Equal rate constants give a 1:1 photostationary mixture:
>>> round(photostationary_ratio(k_AB=2.0, k_BA=2.0), 6) 1.0
- chemistrykit.photochem.photostationary_state(k_AB, k_BA, total_concentration=1.0)[source]#
Solve for the photostationary-state populations of a two-state photoswitch.
Uses the exact algebraic steady-state solution of \(A\rightleftharpoons B\) (the \(t\to\infty\) limit of
chemistrykit.kinetics.systems.networks.reversible_analytic(), with the “forward” rate constant \(k_f=k_{AB}\) and “reverse” \(k_r=k_{BA}\)) rather than integratingphotoswitch_network()to long time numerically – both were checked to agree (see this module’s test suite) as part of developing this function.- Parameters:
k_AB (
float) – Pseudo-first-order photoisomerization rate constants (seephotoswitch_rate_constants()).k_BA (
float) – Pseudo-first-order photoisomerization rate constants (seephotoswitch_rate_constants()).total_concentration (
float) – Total (conserved) \([A]+[B]\).
- Return type:
- Returns:
PhotostationaryStateResult
Examples
>>> result = photostationary_state(k_AB=1.5, k_BA=0.4, total_concentration=1.0) >>> round(result.ratio_B_over_A, 3) 3.75 >>> round(result.A_pss + result.B_pss, 9) 1.0
- chemistrykit.photochem.photoswitch_network(k_AB, k_BA, A0=1.0)[source]#
Build the \(A\rightleftharpoons B\) photoswitch network as a
StoichiometricNetwork.Directly reuses
reversible()– a two-state photoswitch under simultaneous forward/reverse photolysis is, mathematically, the identical reversible first-order reaction network that thermal isomerization would be, just with photochemical rather than Arrhenius rate constants.- Parameters:
k_AB (
float) – Pseudo-first-order photoisomerization rate constants (seephotoswitch_rate_constants()).k_BA (
float) – Pseudo-first-order photoisomerization rate constants (seephotoswitch_rate_constants()).A0 (
float) – Total (initial, all-A) population/concentration.
- Return type:
- Returns:
StoichiometricNetwork
Examples
>>> net = photoswitch_network(k_AB=1.5, k_BA=0.4, A0=1.0) >>> result = net.integrate((0.0, 50.0), dt=1e-3, method="rk4") >>> round(float(result.concentration("B")[-1] / result.concentration("A")[-1]), 3) 3.75
- chemistrykit.photochem.photoswitch_rate_constants(phi_AB, eps_A, phi_BA, eps_B, I0=1.0)[source]#
Pseudo-first-order photoisomerization rate constants in the low-optical-density limit.
\(k_{AB}=2.303\,\Phi_{AB}\,\varepsilon_A\,I_0\) and \(k_{BA}=2.303\,\Phi_{BA}\,\varepsilon_B\,I_0\) (Turro, Ramamurthy & Scaiano, Modern Molecular Photochemistry of Organic Molecules, Ch. 7.6) – see the module docstring for the dilute-limit approximation this relies on.
- Parameters:
phi_AB (
float) – Quantum yield of the \(A\to B\) photoreaction.eps_A (
float) – Molar absorptivity of \(A\) at the irradiation wavelength, in L mol^-1 cm^-1.phi_BA (
float) – Quantum yield of the \(B\to A\) photoreaction.eps_B (
float) – Molar absorptivity of \(B\) at the irradiation wavelength.I0 (
float) – Incident photon flux (or intensity); the ratiok_AB/k_BA– and hence the photostationary ratio – does not depend on this value.
- Return type:
- Returns:
k_AB, k_BA (float)
Examples
Equal quantum yields and absorptivities give equal rate constants:
>>> k_AB, k_BA = photoswitch_rate_constants(phi_AB=0.5, eps_A=1000.0, phi_BA=0.5, eps_B=1000.0) >>> round(k_AB, 6) == round(k_BA, 6) True
- chemistrykit.photochem.rehm_weller_free_energy(E_ox_donor, E_red_acceptor, E00, work_term=0.0)[source]#
Free energy of photoinduced electron transfer, \(\Delta G_{ET}=E_{ox}(D)-E_{red}(A)-E_{00}+w\) (eV).
- Parameters:
- Return type:
- Returns:
float – \(\Delta G_{ET}\), in eV (negative means exergonic).
Examples
>>> round(rehm_weller_free_energy(1.2, -1.9, 3.3), 6) -0.2
- chemistrykit.photochem.rehm_weller_quenching_rate(delta_G, k_diff=20000000000.0, ratio=0.25, dG0_dagger=0.104, T=298.15)[source]#
Rehm-Weller electron-transfer quenching rate constant \(k_q(\Delta G_{ET})\).
- Parameters:
- Returns:
float or ndarray – \(k_q\), in 1/(M*s).
Examples
Strongly exergonic transfer approaches the plateau \(k_d/(1+0.25)=1.6\times10^{10}\), while thermoneutral transfer is more than ten times slower:
>>> round(rehm_weller_quenching_rate(-1.5) / 1e10, 2) 1.5 >>> round(rehm_weller_quenching_rate(-50.0) / 1e10, 2) 1.6 >>> round(rehm_weller_quenching_rate(0.0) / 1e10, 3) 0.128
- chemistrykit.photochem.rotational_correlation_time(viscosity, volume, T)[source]#
Stokes-Einstein-Debye rotational correlation time \(\theta = \eta V/(k_B T)\).
- Parameters:
- Returns:
float or ndarray – \(\theta\), in s.
Examples
A ~1 nm3 fluorophore in water (1 mPa*s) at 298 K rotates in about a quarter of a nanosecond:
>>> round(rotational_correlation_time(1.0e-3, 1.0e-27, 298.15) * 1e9, 3) 0.243
- chemistrykit.photochem.stern_volmer_ratio(Ksv, Q)[source]#
The Stern-Volmer equation: \(I_0/I = 1 + K_{sv}[Q]\).
A plot of the fluorescence-intensity ratio \(I_0/I\) (unquenched over quenched intensity) against quencher concentration \([Q]\) is linear with slope \(K_{sv}\), the Stern-Volmer constant (Lakowicz, Principles of Fluorescence Spectroscopy, 3rd ed., Ch. 8, eq. 8.4). For purely dynamic (collisional) quenching, \(K_{sv}=k_q\tau_0\) (see
dynamic_quenching_constant()) and this same linear relationship also holds for the fluorescence lifetime ratio \(\tau_0/\tau\); for purely static quenching (ground-state complex formation) the intensity ratio is still linear in \([Q]\) (with \(K_{sv}\) reinterpreted as the complex’s formation constant) but the lifetime ratio stays exactly 1 – seeclassify_quenching_mechanism().- Parameters:
- Returns:
float or ndarray – \(I_0/I\) (or, for a dynamic mechanism, equally \(\tau_0/\tau\)).
Examples
At zero quencher, the ratio is exactly 1 (no quenching):
>>> round(float(stern_volmer_ratio(Ksv=5.0, Q=0.0)), 6) 1.0
The ratio grows linearly with quencher concentration:
>>> Q = np.array([0.0, 0.01, 0.02, 0.03]) >>> ratio = stern_volmer_ratio(Ksv=20.0, Q=Q) >>> bool(np.allclose(np.diff(ratio), 20.0 * np.diff(Q))) True
- chemistrykit.photochem.stokes_shift(absorption_max_nm, emission_max_nm)[source]#
Stokes shift in wavenumbers, \(\Delta\tilde\nu = 10^7/\lambda_{abs} - 10^7/\lambda_{em}\) (cm-1).
Stokes (1852) observed that fluorescence is emitted at longer wavelength (lower energy) than the light that excites it; the energy difference is lost to vibrational relaxation and solvent reorganization in the excited state before emission (Lakowicz, Ch. 1.4).
- Parameters:
- Returns:
float or ndarray – Stokes shift in cm-1 (positive for red-shifted emission).
Examples
Absorption at 400 nm and emission at 500 nm:
>>> round(stokes_shift(400.0, 500.0), 6) 5000.0