radicalpy package
Submodules
radicalpy.classical module
Classical kinetics, diffusion, and utility helpers.
This module provides small, focused tools for building and simulating classical (first-order) kinetic schemes, along with diffusion step estimates, LaTeX formatting helpers, and basic 3D random-walk sampling.
Contents:
Diffusion:
get_delta_r(D, dt): RMS relative displacement √(6 D Δt).
LaTeX utilities:
latexify(rate_equations): Build d[Xi]/dt = … strings from a rate map.
latex_eqlist_to_align(eqs): Wrap equations in an
align*environment.Kinetics:
- Rate: Numeric value + LaTeX label with operator overloading that carries
symbolic expressions through +, −, ×, ÷.
- RateEquations: Sparse representation and time evolution (matrix exponential)
for first-order networks; returns EquationRateResult for easy access.
- EquationRateResult: Convenience accessor to sum populations of one or more
states over time.
Visualization:
- reaction_scheme(path, rate_equations): Generate a LaTeX (dot2tex) diagram
of the reaction network.
Random sampling:
random_theta_phi(n): Uniform sampling on the unit sphere.
- randomwalk_3d(…): Monte-Carlo 3D random walk with min/ max-distance
constraints (solution or spherical microreactor).
Key conventions:
Rate maps use: sink_state -> { source_state: Rate|float, … }
The resulting sparse matrix M stores rates at (sink, source), so dP/dt = M P.
Time evolution assumes uniform spacing in
timeand advances via the matrixexponential of the rate matrix over Δt.
Dependencies:
Relies on NumPy and SciPy (sparse) for numerics, graphviz + dot2tex for LaTeX diagram generation.
- class radicalpy.classical.EquationRateResult(result, indices)[source]
Bases:
objectContainer for rate-equation time evolution results.
- class radicalpy.classical.Rate(value, label)[source]
Bases:
objectScalar rate value paired with a LaTeX-formatted symbolic label.
Stores a numeric rate (or expression value) and a human-readable/LaTeX label that tracks algebraic manipulations via operator overloading.
- class radicalpy.classical.RateEquations(rate_equations)[source]
Bases:
objectRepresentation and solver for first-order kinetic rate equations.
- check_initial_states(initial_states)[source]
Validate an initial state population dictionary.
Ensures all keys are valid states and populations sum to 1.
- Parameters:
initial_states (dict) – Mapping from state label to initial population (fractions).
- Raises:
ValueError – If unknown state labels are included.
Notes
Prints a warning if the populations do not sum to unity.
- time_evolution(time, initial_states)[source]
Evolve populations in time under the rate equations.
Solves dP/dt = M P using matrix exponentials at fixed time steps.
- Parameters:
time (np.ndarray) – Array of time points (uniform spacing).
initial_states (dict) – Mapping of state → initial population. Must sum to 1.
- Returns:
Object containing the full population time series and state index mapping.
- Return type:
- radicalpy.classical.get_delta_r(mutual_diffusion, delta_T)[source]
Root-mean-square step length for relative diffusion.
Uses ⟨Δr²⟩ = 6 D Δt for three-dimensional relative motion of two particles (mutual diffusion).
- radicalpy.classical.latex_eqlist_to_align(eqs)[source]
Format LaTeX equations for alignment.
Wraps a list of LaTeX equations in an
align*environment and inserts alignment markers before equal signs.
- radicalpy.classical.latexify(rate_equations)[source]
Convert rate equations into LaTeX strings.
Produces a list of differential equations in LaTeX format, with terms written as sums over outgoing edges.
- radicalpy.classical.random_theta_phi(num_samples=1)[source]
Randomly sample polar (θ) and azimuthal (φ) angles.
Samples directions uniformly over the unit sphere using inverse transform sampling.
- Parameters:
num_samples (int, optional) – Number of random angle pairs to generate. Defaults to 1.
- Returns:
Array of shape (2, num_samples) containing sampled angles in radians: - [0]: θ ∈ [0, π] (polar angle) - [1]: φ ∈ [0, 2π) (azimuthal angle)
- Return type:
np.ndarray
- radicalpy.classical.randomwalk_3d(n_steps, x_0, y_0, z_0, delta_r, r_min, r_max=0)[source]
Simulate a 3D Monte Carlo random walk of a radical pair.
Models diffusion of a radical in solution (
r_max = 0) or inside a spherical microreactor (r_max > 0), enforcing reflective boundaries and minimum approach distance.- Parameters:
n_steps (int) – Number of simulation steps.
x_0 (float) – Initial x-coordinate (m).
y_0 (float) – Initial y-coordinate (m).
z_0 (float) – Initial z-coordinate (m).
delta_r (float) – Step length, e.g., from
get_delta_r(m).r_min (float) – Minimum allowed radical–radical distance (m).
r_max (float, optional) – Maximum radius of microreactor (m). Set to 0 for solution-based, and to a positive value for microreactor-based simulations.
- Returns:
pos (ndarray): Radical positions at each step, shape (n_steps, 3).
dist (ndarray): Radical–radical distances at each step, shape (n_steps,).
angle (ndarray): Polar angles (θ) of displacements, shape (n_steps,).
- Return type:
Tuple[np.ndarray, np.ndarray, np.ndarray]
radicalpy.data module
Data models and utilities for spin-system simulations.
This module provides lightweight classes and helpers that describe the quantum‐mechanical ingredients used throughout the package (isotopes, nuclei, molecules) and common conversions between spin quantum numbers and multiplicities. It also includes accessors for bundled reference data and convenience builders for frequently used composite objects.
Contents:
- Conversions
spin_to_multiplicity()– convert spin quantum numberStomultiplicity
2S+1(validates half‐integer/integer input). -multiplicity_to_spin()– invert the mapping, returningS = (M-1)/2.- Data access
get_data()– locate packaged data files (e.g. JSON databases).- Core data structures
Isotope– look up an isotope’s spin multiplicity andmagnetogyric ratio from the bundled database (CODATA-style values), with helpers like
Isotope.available(). -Hfc– hyperfine coupling container that supports either an isotropic scalar value or a full 3×3 anisotropic tensor, and exposesHfc.isotropic()andHfc.anisotropic(). -Nucleus– a nucleus within a molecule, defined by its magnetogyric ratio, spin multiplicity, and hyperfine couplings; also provides spin (Pauli) operator matrices viaNucleus.pauli(). -FuseNucleus– an effective nucleus formed by fusing several identical nuclei into a direct-sum representation to reduce Hilbert-space dimension; includes utilities for validation and operator construction. -Molecule– collection of nuclei plus a radical (electron‐like spin), with constructors for database-backed molecules (Molecule.fromdb(),Molecule.all_nuclei()) and ad-hoc assemblies (Molecule.fromisotopes()). -Triplet– convenience molecule containing a single S=1 (multiplicity 3) radical and no nuclei.
Units & conventions:
Magnetogyric ratios are commonly expressed as
rad/s/Tin the database;many class properties expose
gamma_mT(rad/s/mT) for convenience. - Hyperfine couplings (HFCs) are in mT. When a 3×3 tensor is provided, the isotropic value is computed astrace(D)/3. - Spin operators follow the usual convention with raising (p), lowering (m), and Cartesian components (x,y,z).
Error handling:
spin_to_multiplicity()validates thatSis integer or half-integer.
IsotoperaisesValueErrorfor unknown symbols and provides
Isotope.available()to enumerate valid options. -Hfc.anisotropicraisesValueErrorwhen no tensor is available.
- class radicalpy.data.FuseNucleus(magnetogyric_ratio, multiplicity, hfc, name=None, spinop=None, weight=None, direct_sum_info=None)[source]
Bases:
NucleusFuse identical nuclei into a single effective nucleus.
This class represents multiple identical nuclei that have been combined into a single effective nucleus for computational efficiency in spin dynamics calculations.
See also: Lindoy et al. Phys. Rev. Lett. 120, 220604 (2019).
Warning
This class should only be instantiated via the
from_nucleiclass method. Direct instantiation using__init__is not recommended for end users. In addtion, for preparing the initial density matrix, theinitial_density_matrixproperty combined withnumpy.kronshould be used instead of the pre-defined initial states such asradicalpy.simulation.State.SINGLETbecause the weights of each direct sum are not properly normalized.Examples
Create a fused nucleus from three identical protons:
>>> protons = [Nucleus.fromisotope("1H", 1.5) for _ in range(3)] >>> protons_fuse = FuseNucleus.from_nuclei(protons) # |J=3/2> ⊕ |J=1/2> >>> assert protons_fuse.magnetogyric_ratio == protons[0].magnetogyric_ratio >>> assert protons_fuse.multiplicity == 4 + 2 >>> assert protons_fuse.hfc.isotropic == protons[0].hfc.isotropic >>> protons_fuse.name 'Fused1H(3)'
>>> protons_fuse.initial_density_matrix array([[0.125, 0. , 0. , 0. , 0. , 0. ], [0. , 0.125, 0. , 0. , 0. , 0. ], [0. , 0. , 0.125, 0. , 0. , 0. ], [0. , 0. , 0. , 0.125, 0. , 0. ], [0. , 0. , 0. , 0. , 0.25 , 0. ], [0. , 0. , 0. , 0. , 0. , 0.25 ]])
>>> protons_fuse.pauli["z"] # z-axis Pauli matrix array([[ 1.5, 0. , 0. , 0. , 0. , 0. ], [ 0. , 0.5, 0. , 0. , 0. , 0. ], [ 0. , 0. , -0.5, 0. , 0. , 0. ], [ 0. , 0. , 0. , -1.5, 0. , 0. ], [ 0. , 0. , 0. , 0. , 0.5, 0. ], [ 0. , 0. , 0. , 0. , 0. , -0.5]])
- classmethod from_nuclei(nuclei)[source]
Create a FuseNucleus from a list of identical nuclei.
This is the recommended way to create a FuseNucleus instance. The method validates that all nuclei are identical and computes the appropriate spin operators for the fused system.
- Parameters:
nuclei (
list[Nucleus]) – List of identical nuclei to fuse (minimum 2 nuclei)- Returns:
The fused nucleus with computed spin operators
- Return type:
- Raises:
ValueError – If fewer than 2 nuclei provided or nuclei are not identical
Examples
>>> h1 = Nucleus.fromisotope("1H", 1.5) >>> h2 = Nucleus.fromisotope("1H", 1.5) >>> h3 = Nucleus.fromisotope("1H", 1.5) >>> h123 = FuseNucleus.from_nuclei([h1, h2, h3])
- property initial_density_matrix: ndarray[tuple[int, ...], dtype[_ScalarType_co]]
Initial density matrix for the fused nucleus.
- Returns:
Diagonal matrix with weights for initial state
- Return type:
NDArray
- class radicalpy.data.Hfc(hfc)[source]
Bases:
objectThe Hfc class represents isotropic and anisotropic HFC values.
- Parameters:
hfc (float | list[list[float]]) – The HFC value. In case of a single
float, only the isotropic value is set. In case of a 3x3 matrix both the isotropic and anisotropic values are stored.
Examples
Initialising the HFC with a 3-by-3 matrix (list of lists):
>>> with open(get_data("molecules/flavin_anion.json"), encoding="utf-8") as f: ... flavin_dict = json.load(f) >>> hfc_3d_data = flavin_dict["data"]["N5"]["hfc"] >>> hfc_3d_obj = Hfc(hfc_3d_data) >>> hfc_3d_obj 0.5141 <anisotropic available>
we can obtain both the isotropic value:
>>> hfc_3d_obj.isotropic 0.5141406139911681
and the anisotropic tensor:
>>> hfc_3d_obj.anisotropic array([[-0.06819637, 0.01570029, 0.08701531], [ 0.01570029, -0.03652102, 0.27142597], [ 0.08701531, 0.27142597, 1.64713923]])
Initialising the HFC with a single float:
>>> with open(get_data("molecules/adenine_cation.json"), encoding="utf-8") as f: ... adenine_dict = json.load(f) >>> hfc_1d_data = adenine_dict["data"]["N6-H1"]["hfc"] >>> hfc_1d_obj = Hfc(hfc_1d_data) >>> hfc_1d_obj -0.63 <anisotropic not available>
we can obtain both the isotropic value:
>>> hfc_1d_obj.isotropic -0.63
but not the anisotropic tensor:
>>> hfc_1d_obj.anisotropic Traceback (most recent call last): ... ValueError: No anisotropic HFC data available.
- class radicalpy.data.Isotope(symbol)[source]
Bases:
objectClass representing an isotope.
- Parameters:
symbol (str) – The symbol/identifier of the isotope in the database.
Examples
Create an isotope using the database.
>>> E = Isotope("E") >>> E Symbol: E Multiplicity: 2 Magnetogyric ratio: -176085963023.0 Details: {'name': 'Electron', 'source': 'CODATA 2018'}
Query the multiplicity:
>>> E.multiplicity 2
Query other details:
>>> E.details {'name': 'Electron', 'source': 'CODATA 2018'}
- classmethod available()[source]
List isotopes available in the database.
Examples
>>> available = Isotope.available() >>> available[:5] ['G', 'E', 'N', 'M', 'P']
>>> Isotope(available[1]) Symbol: E Multiplicity: 2 Magnetogyric ratio: -176085963023.0 Details: {'name': 'Electron', 'source': 'CODATA 2018'}
>>> Isotope(available[2]) Symbol: N Multiplicity: 2 Magnetogyric ratio: -183247171.0 Details: {'name': 'Neutron', 'source': 'CODATA 2018'}
- property gamma_mT
Return gamma value in rad/s/mT.
- class radicalpy.data.Molecule(name='', nuclei=[], radical=E(-176085963023.0, 2, 0.0 <anisotropic not available>), info={})[source]
Bases:
objectRepresentation of a molecule for the simulation.
A molecule is described by a name and a list of nuclei (see
Nucleus). Using the default constructor is cumbersome and not recommended. The preferred way is to use the following convenience methods (click on the method name to see its documentation):- Parameters:
Examples
The default constructor takes an arbitrary name and a list of molecules to construct a molecule.
>>> gamma_1H = 267522.187 >>> gamma_14N = 19337.792 >>> Molecule("kryptonite", [Nucleus(gamma_1H, 2, Hfc(1.0), "Hydrogen"), ... Nucleus(gamma_14N, 3, Hfc(-0.5), "Nitrogen")]) Molecule: kryptonite Nuclei: Hydrogen(267522186.99999997, 2, 1.0 <anisotropic not available>) Nitrogen(19337792.0, 3, -0.5 <anisotropic not available>) Radical: E(-176085963023.0, 2, 0.0 <anisotropic not available>)
Or alternatively:
>>> gammas = [267522.187, 19337.792] >>> multis = [2, 3] >>> hfcs = [1.0, -0.5] >>> names = ["Hydrogen", "Nitrogen"] >>> params = zip(gammas, multis, map(Hfc, hfcs), names) >>> Molecule("kryptonite", [Nucleus(*param) for param in params]) Molecule: kryptonite Nuclei: Hydrogen(267522186.99999997, 2, 1.0 <anisotropic not available>) Nitrogen(19337792.0, 3, -0.5 <anisotropic not available>) Radical: E(-176085963023.0, 2, 0.0 <anisotropic not available>)
- classmethod all_nuclei(name)[source]
Construct a molecule from the database with all nuclei.
- Parameters:
name (str) – A name of the molecule available in the database (see
Molecule.available()).
Examples
>>> Molecule.all_nuclei("flavin_anion") Molecule: flavin_anion Nuclei: 14N(19337792.0, 3, 0.5141 <anisotropic available>) 14N(19337792.0, 3, -0.001275 <anisotropic available>) 14N(19337792.0, 3, -0.03654 <anisotropic available>) 1H(267522187.44, 2, 0.05075 <anisotropic available>) 1H(267522187.44, 2, -0.1371 <anisotropic available>) 1H(267522187.44, 2, -0.1371 <anisotropic available>) 1H(267522187.44, 2, -0.1371 <anisotropic available>) 1H(267522187.44, 2, -0.4403 <anisotropic available>) 1H(267522187.44, 2, 0.4546 <anisotropic available>) 1H(267522187.44, 2, 0.4546 <anisotropic available>) 1H(267522187.44, 2, 0.4546 <anisotropic available>) 1H(267522187.44, 2, 0.009597 <anisotropic available>) 1H(267522187.44, 2, 0.4263 <anisotropic available>) 1H(267522187.44, 2, 0.4233 <anisotropic available>) 1H(267522187.44, 2, -0.02004 <anisotropic available>) 14N(19337792.0, 3, 0.1784 <anisotropic available>) Radical: E(-176085963023.0, 2, 0.0 <anisotropic not available>) Info: {'units': 'mT', 'name': 'Flavin radical anion'}
- classmethod available()[source]
List molecules available in the database.
Examples
>>> available = Molecule.available() >>> available[:4] ['2_6_aqds', 'adenine_cation', 'fad', 'flavin_anion']
- classmethod fromdb(name, nuclei=[])[source]
Construct a molecule from the database.
- Parameters:
name (str) – A name of the molecule available in the database (see
Molecule.available()).nuclei (list[str]) – A list of nuclei names found in the molecule specified by
name.
Examples
>>> Molecule.fromdb("flavin_anion", nuclei=["N14"]) Molecule: flavin_anion Nuclei: 14N(19337792.0, 3, -0.001275 <anisotropic available>) Radical: E(-176085963023.0, 2, 0.0 <anisotropic not available>) Info: {'units': 'mT', 'name': 'Flavin radical anion'}
- classmethod fromisotopes(isotopes, hfcs, name='')[source]
Construct molecule from isotopes.
- Parameters:
Examples
>>> Molecule.fromisotopes(isotopes=["1H", "14N"], ... hfcs=[1.5, 0.9], ... name="kryptonite") Molecule: kryptonite Nuclei: 1H(267522187.44, 2, 1.5 <anisotropic not available>) 14N(19337792.0, 3, 0.9 <anisotropic not available>) Radical: E(-176085963023.0, 2, 0.0 <anisotropic not available>)
- classmethod load_molecule_json(molecule)[source]
Load a molecule definition from the bundled JSON database.
- Parameters:
- Returns:
Parsed JSON content with
infoanddatasections.- Return type:
- Raises:
FileNotFoundError – If the molecule JSON file does not exist.
json.JSONDecodeError – If the file is not valid JSON.
- property semiclassical_std: float
Standard deviation σ of the Schulten–Wolynes (SW) effective hyperfine field.
- Definitions (SW isotropic case):
τ^{-2} = (1/6) Σ_k a_k^2 I_k (I_k + 1) σ = √2 / τ ⇒ σ^2 = (1/3) Σ_k a_k^2 I_k (I_k + 1)
Args:
units: {“omega”, “mT”}, default “omega”
“omega”: return σ_ω in angular-frequency units (same units as a_k).
- “mT”return σ_B in millitesla, i.e., σ_B = σ_ω / γ, where
γ = self.radical.gamma_mT (rad s^-1 mT^-1).
ak_units : {“angular”, “Hz”}, default “angular”
“angular”: each n.hfc.isotropic is already in angular frequency units (e.g., rad s^-1).
“Hz” : each n.hfc.isotropic is in cycles per second; converted internally via 2π.
- Returns:
σ in the requested units.
Notes:
This computes the isotropic SW width (scalar). For anisotropic A-tensors, the isotropic equivalent variance is (1/3) Tr(Σ_ω), where
Σ_ω = (1/3) Σ_k I_k(I_k+1) A_k A_k^T.
If you need full anisotropic sampling, construct Σ_ω explicitly elsewhere.
- class radicalpy.data.Nucleus(magnetogyric_ratio, multiplicity, hfc, name=None)[source]
Bases:
objectA nucleus in a molecule.
Construct a nucleus from an
Isotopeand anHfc.>>> Nucleus.fromisotope("1H", 1.1) 1H(267522187.44, 2, 1.1 <anisotropic not available>)
The default constructor needs the magnetogyric ratio, the multiplicity and the HFC values.
>>> Nucleus(0.001, 2, Hfc(3.0)) Nucleus(1.0, 2, 3.0 <anisotropic not available>)
Additionally a name can also be added.
>>> Nucleus(0.001, 2, Hfc(3.0), "Adamantium") Adamantium(1.0, 2, 3.0 <anisotropic not available>)
- property gamma_mT
Return magnetogyric ratio, \(\gamma\) (rad/s/mT).
- property pauli
Generate Pauli matrices.
Generates the Pauli matrices corresponding to a given multiplicity.
- Parameters:
mult (int) – The multiplicity of the element.
- Returns:
A dictionary containing 6
np.arraymatrices of shape(mult, mult):the unit operator
result["u"],raising operator
result["p"],lowering operator
result["m"],Pauli matrix for x axis
result["x"],Pauli matrix for y axis
result["y"],Pauli matrix for z axis
result["z"].
- Return type:
- radicalpy.data.get_data(suffix='')[source]
Get the directory containing data files.
- Return type:
Traversable
radicalpy.estimations module
Estimation utilities for fields, rates, interactions, and transport.
This module collects closed-form and semi-empirical estimators commonly used in spin chemistry and magnetic resonance modeling. Functions cover theoretical B₁/₂ values, T₁/T₂ relaxation rates (g-anisotropy, hyperfine tensor, and tumbling motion), diffusion/viscosity, dipolar and exchange interactions, kinetic rates (excitation, recombination, re-encounter, electron transfer), triplet relaxation, rotational correlation times, and helpers for fitting autocorrelation functions.
- Major groups:
- B½ / field scales
- Relaxation (g-anisotropy / tumbling)
- Transport & correlation
- Spin interactions
- Dephasing / mixing from trajectories & geometry
- Kinetics
- Units & conventions:
Fields: millitesla (mT) unless otherwise noted; some functions expect tesla (T).
Rates: s⁻¹; angular frequencies in rad·s⁻¹ where specified.
Distances: metres (m); protein electron-transfer
separationis in Å as per literature fits.Temperatures: kelvin (K) unless otherwise noted (e.g., viscosity uses °C input).
Time arrays (
ts,time): seconds (s).g-tensor inputs: principal components per radical, dimensionless.
Many expressions reuse physical constants from a shared
Cnamespace and isotope data viaIsotope("E")(electron); see your codebase for those definitions.
Notes
B½ estimators (
Weller,Golesworthy) are empirical/approximate and assume model conditions stated in the cited works.Tumbling-motion formulas (
Bloembergen–Purcell–Pound) require a distancerand correlation timetau_c; results scale as r⁻⁶.autocorrelation_fituses a fixed set of τ values (log-spaced) with a non-negative multi-exponential fit; the effectivetau_cis the amplitude-weighted sum.Viscosity model (
Volk) is accurate within stated temperature ranges; ensurefrac_glyc∈ [0, 1].Some helpers accept scalars or NumPy arrays and broadcast accordingly.
References
[Atkins et al., Mol. Phys. 27 (6) (1974)](https://doi.org/10.1080/00268977400101361).
[Bloembergen, Purcell & Pound, Phys. Rev. 73, 679 (1948)](https://journals.aps.org/pr/abstract/10.1103/PhysRev.73.679).
[Cavanagh et al. Protein NMR Spectroscopy. Principles and Practice, Elsevier Academic Press (2007)](https://doi.org/10.1016/B978-0-12-164491-8.X5000-3).
[Einstein, Ann. Physik 17, 549–560 (1905)](https://doi.org/10.1002/andp.19053220806).
[Golesworthy et al., J. Chem. Phys. 159, 105102 (2023)](https://doi.org/10.1063/5.0166675).
[Hayashi, Introduction to Dynamic Spin Chemistry (2004)](https://doi.org/10.1142/9789812562654_0015).
[Kattnig et al., New J. Phys. 18, 063007 (2016)](https://iopscience.iop.org/article/10.1088/1367-2630/18/6/063007).
[Maeda et al. Mol. Phys., 117 (19), 2709-2718 (2019)](https://doi.org/10.1080/00268976.2019.1580779).
[McLauchlan et al., Mol. Phys. 73 (2), 241–263 (1991)](https://doi.org/10.1080/00268979100101181).
[Miura et al. J. Phys. Chem. A, 119, 22, 5534-5544 (2015)](https://doi.org/10.1021/acs.jpca.5b02183).
[Moser et al. Biochim. Biophys. Acta Bioenerg. 1797, 1573‐1586 (2010)](https://doi.org/10.1016/j.bbabio.2010.04.441).
[Moser et al., Nature 355, 796–802 (1992)](https://doi.org/10.1038/355796a0).
[O’Dea et al. J. Phys. Chem. A, 109, 5, 869-873 (2005)](https://doi.org/10.1021/jp0456943).
[Player et al., J. Chem. Phys. 153, 084303 (2020)](https://doi.org/10.1063/5.0021643).
[Santabarbara et al., Biochemistry 44 (6), 2119–2128 (2005)](https://pubs.acs.org/doi/10.1021/bi048445d).
[Salikhov, J. Magn. Reson., 63, 271-279 (1985)](https://doi.org/10.1016/0022-2364(85)90316-6).
[Shushin, Chem. Phys. Lett. 181 (2–3), 274–278 (1991)](https://doi.org/10.1016/0009-2614(91)90366-H).
[Steiner & Ulrich, Chem. Rev. 89 (1), 51–147 (1989)](https://doi.org/10.1021/cr00091a003).
[Volk et al., Experiments in Fluids 59, 76 (2018)](https://doi.org/10.1007/s00348-018-2527-y).
[Weller et al., Chem. Phys. Lett. 96 (1), 24–27 (1983)](https://doi.org/10.1016/0009-2614(83)80109-2).
- Requirements:
numpy,scipy(for curve fitting inautocorrelation_fit), and project constants/utilities (C,Isotope,utils) available in scope.
See also
relaxation.py,kinetics.pyfor superoperators using several of these rates.experiments.pyfor simulations (MARY, EPR/ODMR/OMFE) that can consume these estimates.
- radicalpy.estimations.Bhalf_theoretical_hyperfine(sim)[source]
Theoretical B1/2 for radical pairs. Estimated with hyperfine interactions.
Source: Weller et al. Chem. Phys. Lett. 96, 1, 24-27 (1983).
- Parameters:
sim (
HilbertSimulation) – Thesimobject containing the hyperfine coupling constants. It should contain exactly two molecules.- Returns:
The B1/2 value (mT).
- Return type:
Todo
Change
simto a list of molecules.
- radicalpy.estimations.Bhalf_theoretical_relaxation(kstd, krec)[source]
Theoretical B1/2 for radical pairs. Estimated with spin dephasing rate.
Source: Golesworthy et al. J. Chem. Phys. 159, 105102 (2023).
- radicalpy.estimations.Bhalf_theoretical_relaxation_delay(kstd, krec, td)[source]
Theoretical B1/2 for radical pairs. Estimated with spin dephasing rate and pump-probe delay time.
Source: Golesworthy et al. J. Chem. Phys. 159, 105102 (2023).
- radicalpy.estimations.T1_relaxation_rate_g_tensor(g_tensors, B, tau_c)[source]
T1 relaxation rate.
Estimate T1 relaxation rate based on tau_c and g-tensor anisotropy.
- radicalpy.estimations.T1_relaxation_rate_hyperfine_tensor(hyperfine_tensor, B, tau_c)[source]
T1 relaxation rate.
Estimate T1 relaxation rate based on tau_c and hyperfine tensor anisotropy.
- radicalpy.estimations.T1_relaxation_rate_tumbling_motion(tau_c, B0, r)[source]
T1 relaxation rate.
Estimate T1 relaxation rate based on tau_c and r distance between radicals.
Source: Bloembergen, Purcell, and Pound, Phys. Rev. 73, 679 (1948).
- radicalpy.estimations.T2_relaxation_rate_g_tensor(g_tensors, B, tau_c)[source]
T2 relaxation rate.
Estimate T2 relaxation rate based on tau_c and g-tensor anisotropy.
- radicalpy.estimations.T2_relaxation_rate_hyperfine_tensor(hyperfine_tensor, B, tau_c)[source]
T2 relaxation rate.
Estimate T2 relaxation rate based on tau_c and hyperfine tensor anisotropy.
- radicalpy.estimations.T2_relaxation_rate_tumbling_motion(tau_c, B0, r)[source]
T2 relaxation rate.
Estimate T2 relaxation rate based on tau_c and r distance between radicals.
Source: Bloembergen, Purcell, and Pound, Phys. Rev. 73, 679 (1948).
- radicalpy.estimations.aqueous_glycerol_viscosity(frac_glyc, temp)[source]
Viscosity of aqueous glycerol solutions.
Gives a good approximation for temperatures in the range 0-100°C.
- radicalpy.estimations.autocorrelation_fit(ts, trajectory, tau_begin, tau_end, num_exp=100, normalise=False)[source]
Fit multiexponential to autocorrelation plot and calculate the effective rotational correlation time.
- Parameters:
ts (np.ndarray) – Time interval (x-axis of the
trajectory) (s).trajectory (np.ndarray) – The raw data which will be fit and used to calculate
tau_c.tau_begin (float) – Initial lag time (s).
tau_end (float) – Final lag time (s).
num_exp (int) – Number of exponential terms in the multiexponential fit (default=100).
normalise (bool) – When set to true, the autocorrelation is normalised (default=False).
- Returns:
fitis the multiexponential fit to the autocorrelation.tau_cis the effective rotational correlation time.
- Return type:
Thank you, Gesa Grüning!
- radicalpy.estimations.diffusion_coefficient(radius, temperature, eta)[source]
Diffusion coefficient.
The Stokes-Einstein relation.
- radicalpy.estimations.dipolar_interaction_MC(r, theta)[source]
Dipolar interaction for Monte Carlo trajectories.
Sources:
- radicalpy.estimations.dipolar_interaction_anisotropic(r)[source]
Anisotropic dipolar coupling.
Point dipole approximation is used.
- Parameters:
- Return type:
- Returns:
The dipolar coupling tensor in millitesla (mT).
Todo
np.ndarray not implemented.
dipolar * diagfails.
- radicalpy.estimations.dipolar_interaction_anisotropic_from_dipolar_vector(dipolar_vector, dipolar_prefactor=3.2698330438079847e-19, *, units='Å')[source]
Full electron–electron dipolar coupling tensor (angular frequency, rad/s).
Thank you, Luca Gerhards!
Computes
\[D = prefactor * [ (|r|^2 I) - 3 r rᵀ ] / |r|^5\]where the default
prefactoris\[(μ0 / 4π) * ħ * γ_e^2\]with γ_e the electron gyromagnetic ratio (rad s⁻¹ T⁻¹). The input displacement vector can be given in Å or m.
- Parameters:
dipolar_vector (
ndarray) – Displacement vector r from spin 2 to spin 1 (Cartesian).dipolar_prefactor (
float) – Physical prefactor. Default corresponds to two electrons and returns the tensor in angular frequency units (rad/s).units (
str) – Units ofdipolar_vector. {“Å”,”m”}, optional. Default “Å” (Angstrom).
- Return type:
- Returns:
Dipolar coupling tensor in angular frequency units (rad/s).
- radicalpy.estimations.dipolar_interaction_anisotropic_from_dipolar_vector_without_prefactor(dipolar_vector, *, units='Å')[source]
Geometry-only dipolar tensor kernel (no physical prefactor).
Thank you, Luca Gerhards!
Given a displacement vector r between two spins, returns the 3×3 geometric tensor
\[T_{geom} = [ (|r|^2 I) - 3 r rᵀ ] / |r|^5 = (I - 3 r̂ r̂ᵀ) / |r|^3\]
- radicalpy.estimations.dipolar_interaction_isotropic(r)[source]
Isotropic dipolar coupling.
Point dipole approximation is used.
Source: Santabarbara et al. Biochemistry, 44, 6, 2119–2128 (2005).
- radicalpy.estimations.dipolar_interaction_point_dipole(r12)[source]
Dipolar interaction for point dipole approximation.
- Parameters:
r12 (tuple[float, float, float]) – The interradical separation (m).
- Returns:
The 3x3 dipolar coupling tensor in millitesla (mT)
- Return type:
np.ndarray
Example
>>> dipolar_interaction_point_dipole(r12=(1e-09, 0, 0)) array([[-3.71390565, -0. , -0. ], [-0. , 1.85695282, -0. ], [-0. , -0. , 1.85695282]])
- radicalpy.estimations.exchange_interaction_in_protein(r, beta=14000000000.0, J0=9700000000.0)[source]
Exchange interaction for radical pairs in proteins.
- radicalpy.estimations.exchange_interaction_in_solution(r, beta=4.9e-11, J0rad=1.7e+17)[source]
Exchange interaction for radical pairs in solution.
- radicalpy.estimations.exchange_interaction_in_solution_MC(r, beta=20000000000.0, J0=-570)[source]
Exchange interaction for Monte Carlo trajectories.
Sources:
- radicalpy.estimations.g_tensor_relaxation_rate(tau_c, g1, g2)[source]
g-tensor relaxation rate.
To be used with
radicalpy.relaxation.RandomFields.
- radicalpy.estimations.k_STD_microreactor(D, V, d=5e-10, J0=100000000000.0, alpha=20000000000.0)[source]
ST-dephasing rate for radical pairs in microreactors.
Source: Shushin, Chem. Phys. Lett., 181, 2–3, 274-278 (1991).
- Parameters:
- Returns:
The ST-dephasing rate (1/s).
- Return type:
- radicalpy.estimations.k_constant(r, gamma)[source]
K constant used for T1 and T2 estimation.
K constant used to calculate T1 and T2 relaxation
radicalpy.estimations.k_t1_relaxation_tumbling_motionradicalpy.estimations.k_t2_relaxation_tumbling_motion.Source: Bloembergen, Purcell, and Pound, Phys. Rev. 73, 679 (1948).
- radicalpy.estimations.k_electron_transfer(separation, driving_force=-1, reorganisation_energy=1)[source]
Electron transfer rate.
The default values (when
-driving_force == reorganisation_energy) return the maximum electron transfer rate.Source: Moser et al. Biochim. Biophys. Acta Bioenerg. 1797, 1573‐1586 (2010).
- radicalpy.estimations.k_excitation(wavelength, beam_radius, absorbance, concentration, laser_power, path_length)[source]
Groundstate excitation rate.
- Parameters:
wavelength (float) – The excitation wavelength (m).
beam_radius (float) – Radius of the beam spot (m).
absorbance (float) – Absorbance of the sample (OD).
concentration (float) – Concentration of the sample (mol/m^3).
laser_power (float) – The excitation laser power (W).
pathlength (float) – The path length of the sample cell (m).
- Returns:
The excitation rate (1/s).
- Return type:
- radicalpy.estimations.k_triplet_relaxation(B0, tau_c, D, E)[source]
Excited triplet state relaxation rate.
Source: Atkins et al. Mol. Phys., 27, 6 (1974).
- Parameters:
- Returns:
The excited triplet state relaxation rate (1/s).
- Return type:
- radicalpy.estimations.rotational_correlation_time_for_molecule(radius, temp, eta=0.00089)[source]
Rotational correlation time.
Rotational correlation time is the average time it takes for a molecule (smaller than a protein) to rotate one radian. For proteins see
radicalpy.estimations.rotational_correlation_time_for_protein.To calculate viscosity (eta) for glycerol-water mixtures see
radicalpy.estimations.aqueous_glycerol_viscosity.
- radicalpy.estimations.rotational_correlation_time_for_protein(Mr, temp, eta=0.00089)[source]
Rotational correlation time.
Rotational correlation time is the average time it takes for a protein to rotate one radian. For small molecules see
radicalpy.estimations.rotational_correlation_time_for_molecule.To calculate viscosity (eta) for glycerol-water mixtures see
radicalpy.estimations.aqueous_glycerol_viscosity.Source: Cavanagh et al. Protein NMR Spectroscopy. Principles and Practice, Elsevier Academic Press (2007).
radicalpy.experiments module
Experimental simulation routines for spin chemistry and magnetic resonance.
This module groups high-level experiment drivers and inner loops used to simulate anisotropy scans, magnetically affected reaction yields (MARY), EPR/ODMR/OMFE time-domain responses, RYDMR, steady-state signals, simple NMR lineshapes, OOP-ESEEM envelopes, and hybrid semiclassical/quantum MARY variants. Most functions assemble Hamiltonian/Liouvillian terms, apply kinetics/relaxation superoperators, propagate density matrices, and return structured results (dicts, arrays) ready for plotting and analysis.
- Functions:
anisotropy: Full anisotropy experiment wrapper; returns time evolutions and yields.anisotropy_loop: Inner loop over (θ, φ) orientations; propagates and returns product probabilities.cidnp: CIDNP polarisation vs field for S–T0 mixing.coherent_control: Coherent control of radical pair spin dynamics.epr: CW/AC time-domain EPR vs B0 with B1 drive and frequency offset.field_switching: Nanosecond field switching experiment (SEMF).kine_quantum_mary: Hybrid kinetic+quantum MARY with stochastic hyperfine sampling.magnetic_field_loop: Inner loop over swept field B; returns time-resolved density matrices.magnetic_field_loop_semiclassical: Semiclassical inner loop over swept field B; returns time-resolved density matrices.mary_lfe_hfe: Post-process product probabilities → MARY, low-/high-field effects (%).mary: MARY vs B (time-domain propagation + yields + normalised response).mary_semiclassical: Semiclassical MARY vs B (time-domain propagation + yields + normalised response).modulated_mary_brute_force: Lock-in MARY via phase randomisation and numerical integration.nmr: Simple 1D NMR synthesiser (FID → FFT) with multiplets and T2 decay.odmr: ODMR vs RF frequency (B1_freq) at fixed B0.omfe: Oscillating magnetic field effect vs RF frequency in transverse field.oop_eseem: Out-of-phase ESEEM envelope via Gauss–Legendre quadrature.rydmr: Reaction yield–detected magnetic resonance vs static field.semiclassical_mary: Semiclassical MARY with explicit population channels.steady_state_mary: Steady state via linear solve of Liouvillian (with ZFS/exchange/Zeeman).
- Usage pattern:
Build Hamiltonian components from your
Simulationobject (e.g., Zeeman, exchange J, dipolar D, hyperfine ± anisotropy).(Optional) Assemble kinetics/relaxation terms and apply to the Liouvillian via
apply_liouville_hamiltonian_modifiers(...).Propagate using an inner loop (
anisotropy_loop,magnetic_field_loop) or a high-level routine (mary,epr,odmr,omfe,rydmr).Post-process to yields, MARY/LFE/HFE, or spectra; the helpers return a dict containing inputs, intermediates, and outputs.
- Args conventions (selected):
time(np.ndarray): Uniform time grid (s).B,B0,B1,B1_freq(array/float): Fields in mT unless noted.D,E,J(float): Dipolar/ZFS/Exchange parameters (mT) for Zeeman-scaled forms, or (rad/s) where indicated (e.g.,oop_eseem).theta,phi(float or np.ndarray): Polar/azimuthal angles (rad) for anisotropy.kinetics,relaxations(list): Collections of (super)operators compatible with the simulation API; applied in Liouville space.multiplets(NMR):[n_nuc, chem_shift_Hz, multiplicity, J_Hz]per line.
- Returns (typical):
Dictionaries with keys such as:
time,B/B0/B1_freq, angles,rhos,time_evolutions(product probabilities),product_yields,product_yield_sums, and derived metrics (MARY,LFE,HFE).Arrays for specialised routines (e.g.,
(ppm, spectrum)fornmr, MARY tensors for lock-in models, steady-state vectors for linear solves).
Notes
Spaces and shapes: Propagation may occur in Liouville space; helpers such as
_square_liouville_rhosare used to reshape back to Hilbert(N, N)for inspection. See each function’s docstring for shapes.Units: Field parameters are mT unless otherwise specified; NMR chemical shifts are handled via Hz/MHz → ppm conversions;
oop_eseemuses rad/s.Performance: Inner loops precompute unit-field Zeeman operators and reuse sparse CSC matrices for repeated exponentials where possible.
CIDNP models:
cidnp_model∈ {“a”,”b”,”c”} selects exponential, truncated diffusion, or full diffusion variants and requiresksoralphaaccordingly.Lock-in MARY:
modulated_mary_brute_forceperforms phase randomisation, envelope weighting, and numerical integration to estimate RMS harmonic responses.
References
[Antill & Vatai, J. Chem. Theory Comput. 20, 9488–9499 (2024)](https://doi.org/10.1021/acs.jctc.4c00887).
[Konowalczyk et al., PCCP 23, 1273–1284 (2021)](https://doi.org/10.1039/D0CP04814C).
[Maeda et al., Mol. Phys. 104, 1779–1788 (2006)](https://doi.org/10.1080/14767050600588106).
[Masuzawa et al., J. Chem. Phys. 152, 014301 (2020)](https://doi.org/10.1063/1.5131557).
- Requirements:
numpy,scipy(sparse algebra, matrix exponentials), and a simulation object implementing APIs such aszeeman_hamiltonian,exchange_hamiltonian,dipolar_hamiltonian,hyperfine_hamiltonian,projection_operator,convert,time_evolution,product_probability,product_yield, and modifier application hooks.
See also
plot.pyfor visualization helpers.kinetics.py,relaxation.pyfor incoherent processes added to Liouvillians.Individual function docstrings within this module for precise signatures, units, and return structures.
- radicalpy.experiments.anisotropy(sim, init_state, obs_state, time, theta, phi, B0, D, J, kinetics=[], relaxations=[])[source]
Anisotropy experiment.
- Parameters:
init_state (State) – Initial
Stateof the density matrix.obs_state (State) – Observable
Stateof the density matrix.time (np.ndarray) – An sequence of (uniform) time points, usually created using
np.arangeornp.linspace.theta (np.ndarray) – rotation (polar) angle between the external magnetic field and the fixed molecule. See
zeeman_hamiltonian_3d.phi (np.ndarray) – rotation (azimuth) angle between the external magnetic field and the fixed molecule. See
zeeman_hamiltonian_3d.B0 (float) – External magnetic field intensity (milli Tesla) (see
zeeman_hamiltonian).D (np.ndarray) – Dipolar coupling constant (see
dipolar_hamiltonian).J (float) – Exchange coupling constant (see
exchange_hamiltonian).kinetics (list) – A list of kinetic (super)operators of type
radicalpy.kinetics.HilbertKineticsBaseorradicalpy.kinetics.LiouvilleKineticsBase.relaxations (list) – A list of relaxation superoperators of type
radicalpy.relaxation.LiouvilleRelaxationBase.
- Returns:
time: the original
timeobjectB0:
B0parametertheta:
thetaparameterphi:
phiparameter- rhos: tensor of sequences of time evolution of density
matrices
time_evolutions: product probabilities
product_yields: product yields
product_yield_sums: product yield sums
- Return type:
- radicalpy.experiments.anisotropy_loop(sim, init_state, obs_state, time, B0, H_base, theta, phi)[source]
Inner loop of anisotropy experiment.
- Parameters:
sim (LiouvilleSimulation) – Simulation object.
init_state (State) – Initial
Stateof the density matrix.obs_state (State) – Observable
Stateof the density matrix.time (np.ndarray) – An sequence of (uniform) time points, usually created using
np.arangeornp.linspace.B0 (float) – External magnetic field intensity (milli Tesla) (see
zeeman_hamiltonian).H_base (np.ndarray) – A “base” Hamiltonian, i.e., the Zeeman Hamiltonian will be added to this base, usually obtained with
total_hamiltonianandB0=0.theta (np.ndarray) – rotation (polar) angle between the external magnetic field and the fixed molecule. See
zeeman_hamiltonian_3d.phi (np.ndarray) – rotation (azimuth) angle between the external magnetic field and the fixed molecule. See
zeeman_hamiltonian_3d.
- Returns:
A tensor which has a series of density matrices for each angle
thetaandphiobtained by runningtime_evolutionfor each of them (withtimetime-steps,B0magnetic intensity).- Return type:
np.ndarray
- radicalpy.experiments.cidnp(B0, deltag, cidnp_model, nucleus_of_interest, donor_hfc_spinhalf, acceptor_hfc_spinhalf, donor_hfc_spin1, acceptor_hfc_spin1, ks=None, alpha=None)[source]
CIDNP polarisation vs field for a radical pair with S-T0 mixing only.
- Parameters:
B0 (
ndarray) – External magnetic field (T).deltag (
float) – Difference in g-value between the acceptor and donor.cidnp_model (
str) – Choose between CIDNP kinetic models. a) Exponential model. b) Truncated diffusion model. c) Full diffusion model.ks (
Optional[float]) – Decay rate constant for the Exponential model (1/s).alpha (
Optional[float]) – Parameter for the full diffusion model.nucleus_of_interest (
int) – The nucleus chosen for the simulation.donor_hfc_spinhalf (
float) – spin 1/2 HFCs (1H) for the donor (mT).acceptor_hfc_spinhalf (
float) – spin 1/2 HFCs (1H) for the acceptor (mT).donor_hfc_spin1 (
float) – spin 1 HFCs (14N) for the donor (mT).acceptor_hfc_spin1 (
float) – spin 1 HFCs (14N) for the acceptor (mT).
- Return type:
- Returns:
B0 (T) and polarisation (polarisation at each field point)
- radicalpy.experiments.coherent_control(sim, *, init_state, obs_state, time, sticks_A_freq, sticks_A_int, sticks_B_freq, sticks_B_int, B1_G=200.0, g_e=2.0023, k_s=2000000.0, J=0.0, dt_override=None, u_max_factor=1.0, u_smooth=0.2)[source]
Coherent microwave-feedback simulation.
It treats the radical pair as an ensemble of frequency-configurations. Each configuration (A-stick × B-stick) is propagated in Hilbert space under
\[H_{ij}(t) = \omega_{A,i} S_{zA} + \omega_{B,j} S_{zB} + H_J + u(t)(S_{xA}+S_{xB})\]where
the frequencies \(\omega_{A,i}\), \(\omega_{B,j}\) come from the stick lists
sticks_A_freqandsticks_B_freq(in rad/s),the weights of each configuration are the products of the normalised stick intensities
sticks_A_intandsticks_B_int,u(t)is a global feedback field, common to all configurations, built from the ensemble signal.
The feedback field is computed at every time step as
\[u_\text{raw}(t) = B_{1c}\, \sum_{ij} w_{ij} \, \Im \operatorname{Tr} \left[ P_\text{target}( V \rho_{ij} - \rho_{ij} V ) \right],\]then clipped to
±u_max_factor * B1cand optionally smoothed in time by an exponential smoother. HereV = S_xA + S_xBis the microwave operator,P_target = |αβ⟩⟨αβ| + |βα⟩⟨βα|is the “target” projector,B1c = g_e μ_B B1_T / ħis the on-resonance Rabi frequency, withB1_Ggiven in gauss.
Time propagation is done configuration-by-configuration with an RK4 step on the user-supplied time grid.
- Parameters:
sim – HilbertSimulation
init_state – State Spin state used both for the initial density.
obs_state – State Spin state to monitor as an observable population during the run. A projector onto this state is evaluated on every configuration and then averaged over the ensemble.
time (
ndarray) – ndarray 1D array of time points (s). The integration advances along this grid. Ifdt_overrideis not given, the step size is inferred fromtime[1] - time[0].sticks_A_freq (
Sequence[float]) – sequence of float Stick frequencies for radical A and radical B, respectively, in angular frequency units (rad/s), e.g.2*pi*1e6*[ ... MHz ... ].sticks_B_freq (
Sequence[float]) – sequence of float Stick frequencies for radical A and radical B, respectively, in angular frequency units (rad/s), e.g.2*pi*1e6*[ ... MHz ... ].sticks_A_int (
Sequence[float]) – sequence of float Corresponding intensities for the two stick sets. Each list is normalised so that its entries sum to 1. The weight of configuration (i, j) is then the product of the two normalised intensities.sticks_B_int (
Sequence[float]) – sequence of float Corresponding intensities for the two stick sets. Each list is normalised so that its entries sum to 1. The weight of configuration (i, j) is then the product of the two normalised intensities.B1_G (
float) – float, optional Microwave amplitude in gauss. Default is200.0.g_e (
float) – float, optional Electron g-value used in the Rabi-frequency conversion. Default 2.0023.k_s (
float) – float, optional Haberkorn recombination rate applied with respect toinit_state(s⁻¹). Default2e6.J (
float) – float, optional Exchange coupling (mT).dt_override (
Optional[float]) – float, optional If provided, use this as the integration time step instead oftime[1] - time[0].u_max_factor (
float) – float, optional Hard clip on the feedback:|u(t)| ≤ u_max_factor * B1c. Useful to suppress large initial spikes. Default 1.0.u_smooth (
float) – float, optional Exponential smoothing factor for the feedback in (0, 1). Larger values give smoother control fields. Default 0.2.
- Return type:
- Returns:
- dict of str -> ndarray
A dictionary with the following entries:
"time": Time axis (s)"u": Feedback field (rad/s)"target": nsemble-average target population"each_target": Target population per configuration"weights": Configuration weights"obs": Ensemble-average population ofobs_state"each_obs": Per-configuration population ofobs_state"population": Ensemble-average trace of ρ"each_population": Per-configuration trace
- radicalpy.experiments.epr(sim, init_state, obs_state, time, D, J, B0, B1, B1_freq, B0_axis='z', B1_axis='x', kinetics=[], relaxations=[], hfc_anisotropy=False)[source]
Electron paramagnetic resonance (EPR) time‐domain simulation with CW/AC fields.
- Parameters:
sim (HilbertSimulation) – Simulation object.
init_state (State) – Initial
Stateof the density matrix.obs_state (State) – Observable
Stateof the density matrix.time (np.ndarray) – Sequence of (uniform) time points.
D (float) – Dipolar coupling constant (mT).
J (float) – Exchange coupling constant (mT).
B0 (np.ndarray) – Static magnetic field sweep (mT) applied along
B0_axis.B1 (float) – RF/AC field amplitude (mT) applied along
B1_axis.B1_freq (float) – RF/AC angular frequency offset (mT). Implemented as an effective Zeeman term with negative sign on
B0_axis.B0_axis (str) – Axis for
B0Zeeman term ('x'|'y'|'z').B1_axis (str) – Axis for
B1Zeeman term ('x'|'y'|'z').kinetics (list) – List of kinetic superoperators.
relaxations (list) – List of relaxation superoperators.
hfc_anisotropy (bool) – Include anisotropic hyperfine Hamiltonian if True.
- Returns:
time: original
timeB0: sweep values
B0_axis: axis of
B0B1: RF amplitude
B1_axis: axis of
B1B1_freq: RF angular frequency offset
B1_freq_axis: axis used for the offset term
rhos: density matrices (squared to Hilbert shape)
time_evolutions: product probabilities vs time & field
product_yields: integrated product yields
product_yield_sums: scalar yield per field
MARY: normalised magnetoresponse
LFE: low field effect (%)
HFE: high field effect (%)
- Return type:
- radicalpy.experiments.field_switching(sim, *, B_on=2.0, B_off=0.0, init_state=State.TRIPLET, dt=1e-09, n_offsets=100, offset_step=10, pulse_width_steps=800, k_rec=50000000.0, k_esc=2000000.0, J=0.0, D=0.0)[source]
Nanosecond field-switching experiment. Switched External Magnetic Field (SEMF) simulation.
- Parameters:
sim (
HilbertSimulation) – Sim object.B_on (
float) – Magnetic fields (mT) for the “before switch” part and the “after switch” part, respectively.B_off (
float) – Magnetic fields (mT) for the “before switch” part and the “after switch” part, respectively.dt (
float) – Time step (s).n_offsets (
int) – Number of different switch times to scan.offset_step (
int) – Number of time steps to increase the switch time by each iteration.pulse_width_steps (
int) – Length (in steps) of the second part of the evolution (the “pulse”).k_rec (
float) – Haberkorn recombination and escape rates (s⁻¹).k_esc (
float) – Haberkorn recombination and escape rates (s⁻¹).J (
float) – Exchange and dipolar couplings.D (
float) – Exchange and dipolar couplings.
- Returns:
time: (T,),switch_times: (n_offsets,),TA_on: (T, n_offsets),TA_off: (T, n_offsets),TA_diff: (T, n_offsets),
- Return type:
A
dictwith the following keys (dims)
- radicalpy.experiments.kine_quantum_mary(sim, num_samples, init_state, radical_pair, ts, Bs, D, J, kinetics, relaxations)[source]
Kinetic + quantum MARY (hybrid) with sampling over stochastic hyperfine fields.
Source: Antill and Vatai, J. Chem. Theory Comput. 20, 21, 9488–9499 (2024).
- Parameters:
sim (SemiclassicalSimulation) – Simulation object.
num_samples (int) – Number of stochastic hyperfine field realisations.
init_state (ArrayLike) – Initial population vector/state.
radical_pair (list) – Slice indices
[i0, i1]defining the radical-pair subspace into which the Liouvillian is inserted.ts (np.ndarray) – Time grid (s).
Bs (ArrayLike) – Magnetic field sweep values (mT).
D (float) – Dipolar coupling constant (mT).
J (float) – Exchange coupling constant (mT).
kinetics (ArrayLike) – Kinetic matrix to be added to the Liouvillian.
relaxations (list[ArrayLike]) – List of relaxation superoperators.
- Returns:
ts: time grid
Bs: field sweep
yield: complex yield tensor with shape
(len(ts), len(init_state), len(Bs))
- Return type:
- radicalpy.experiments.magnetic_field_loop(sim, init_state, time, H_base, B, B_axis, theta=None, phi=None, hfc_anisotropy=False)[source]
Generate density matrices (rhos) for MARY.
- Parameters:
sim (HilbertSimulation) – Simulation object.
init_state (State) – Initial
Stateof the density matrix.time (np.ndarray) – A sequence of (uniform) time points, usually created using
np.arangeornp.linspace(s).H_base (np.ndarray) – A “base” Hamiltonian or Liouvillian that does not include the swept Zeeman term (e.g. the result of
total_hamiltonian(...)withB0=0and any static terms).B (np.ndarray) – Magnetic-field sweep values (mT). Each value scales a precomputed unit-field Zeeman operator on
B_axis.B_axis (str) – Axis of the Zeeman term (
'x'|'y'|'z').theta (float, optional) – Rotation (polar) angle between the external magnetic field and the fixed molecular frame. See
zeeman_hamiltonian_3d.phi (float, optional) – Rotation (azimuth) angle between the external magnetic field and the fixed molecular frame. See
zeeman_hamiltonian_3d.hfc_anisotropy (bool) – Reserved for API symmetry; anisotropic hyperfine terms should be included in
H_baseif required (not used here).
- Returns:
A tensor of density matrices with shape
(len(B), len(time), *rho_shape), whererho_shapeis(N, N)for Hilbert-space propagation and(N,)for Liouville-space propagation. For each field valueB[i], the time evolution is obtained by propagating underH = H_base + B[i] * H_Z(1.0)withH_Z(1.0)the unit-field Zeeman operator onB_axis.- Return type:
np.ndarray
Notes
The Zeeman operator for a unit field is computed once and reused for all
B[i], then scaled and added toH_base.Propagation uses a CSC sparse representation for efficiency.
- radicalpy.experiments.magnetic_field_loop_semiclassical(sim, init_state, time, B, H_base, kinetics=[], relaxations=[], theta=None, phi=None, num_samples=None)[source]
Generate density matrices (rhos) for MARY using a semiclassical ensemble of hyperfine realisations and incoherent processes.
- Parameters:
sim (HilbertSimulation) – Simulation object.
init_state (State) – Initial
Stateof the density matrix.time (np.ndarray) – A sequence of (uniform) time points, usually created using
np.arangeornp.linspace(s).B (np.ndarray) – Magnetic-field sweep values (mT). Each value scales a precomputed unit-field Zeeman operator summed over all radicals on the laboratory
'z'axis (after applying the optionaltheta/phirotation).H_base (np.ndarray) – A “base” Hamiltonian that does not include the swept Zeeman term, e.g., the result of
total_hamiltonian(...)withB0=0plus any static terms.kinetics (list[HilbertIncoherentProcessBase]) – List of incoherent kinetic processes (e.g., recombination, intersystem crossing) to be added as Liouvillian modifiers. Defaults to
[].relaxations (list[HilbertIncoherentProcessBase]) – List of relaxation/dephasing processes to be added as Liouvillian modifiers. Defaults to
[].theta (float, optional) – Rotation (polar) angle between the external magnetic field and the fixed molecular frame. See
zeeman_hamiltonian_3d.phi (float, optional) – Rotation (azimuth) angle between the external magnetic field and the fixed molecular frame. See
zeeman_hamiltonian_3d.num_samples (int, optional) – Number of semiclassical hyperfine realizations used for ensemble averaging. If
None, the simulator’s default is used bysim.semiclassical_HHs(...).
- Returns:
A tensor of density matrices with shape
(len(B), len(time), *rho_shape), whererho_shapeis(N, N)for Hilbert-space propagation and(N,)for Liouville-space propagation. For each field valueB[i], the result is the sample-average overnum_samplessemiclassical HamiltoniansH_HH[k]of the time evolution underH_t = H_base + B[i] * H_Z(1.0) + H_HH[k],with incoherent
kinetics + relaxationsapplied as Liouvillian modifiers.- Return type:
np.ndarray
Notes
The unit-field Zeeman operator is constructed by summing the per-radical contributions on the
'z'axis (after rotation bytheta/phi) and is then scaled by eachB[i].An ensemble
HHs = sim.semiclassical_HHs(num_samples)provides the semiclassical hyperfine Hamiltonians used for averaging.sim.convert(...)maps the total Hamiltonian to the working representation (Hilbert→Liouville if required); incoherent processes are then added viasim.apply_liouville_hamiltonian_modifiers(...).Propagation uses a CSC sparse representation for efficiency, and the final array at index
iis the mean across all samples for that field value.
- radicalpy.experiments.mary(sim, init_state, obs_state, time, B, D, J, kinetics=[], relaxations=[], theta=None, phi=None, hfc_anisotropy=False)[source]
Magnetically affected reaction yield (MARY) simulation over a magnetic field sweep.
- Parameters:
sim (HilbertSimulation) – Simulation object.
init_state (State) – Initial
Stateof the density matrix.obs_state (State) – Observable
Stateof the density matrix.time (np.ndarray) – Sequence of (uniform) time points (s).
B (np.ndarray) – Magnetic field sweep values (mT) along z-axis by default.
D (float) – Dipolar coupling constant (mT).
J (float) – Exchange coupling constant (mT).
kinetics (list) – List of kinetic superoperators.
relaxations (list) – List of relaxation superoperators.
theta (float, optional) – Polar angle for the Zeeman term (anisotropy).
phi (float, optional) – Azimuthal angle for the Zeeman term (anisotropy).
hfc_anisotropy (bool) – Include anisotropic hyperfine Hamiltonian if True.
- Returns:
time: original
timeB: sweep values
theta:
thetaparameterphi:
phiparameterrhos: density matrices (squared to Hilbert shape)
time_evolutions: product probabilities
product_yields: product yields
product_yield_sums: product yield sums
MARY: normalised magnetoresponse
LFE: low field effect (%)
HFE: high field effect (%)
- Return type:
- radicalpy.experiments.mary_lfe_hfe(obs_state, B, product_probability_seq, dt, k, c=0.0)[source]
Calculate MARY, LFE, HFE.
- radicalpy.experiments.mary_semiclassical(sim, init_state, obs_state, time, B, D, J, kinetics=[], relaxations=[], theta=None, phi=None, num_samples=None, c=None)[source]
Compute a MARY (Magnetically Affected Reaction Yield) curve using the semiclassical simulation pipeline.
This routine constructs a field-independent base Hamiltonian \(H = H_J + H_D\) from the exchange and dipolar interactions, then performs a semiclassical magnetic-field sweep, evolving the state from
init_stateover the time grid for each field value inB. Product probabilities are converted to integrated yields and post-processed into the standard MARY, low-field effect (LFE), and high-field effect (HFE) metrics.- Parameters:
sim (
SemiclassicalSimulation) – SemiclassicalSimulation Semiclassical simulator providing Hamiltonian terms and evolution back-ends (including stochastic sampling where applicable).init_state (
State) – State Initial state for time evolution (e.g.,State.SINGLET).obs_state (
State) – State Observable (product) state whose probability/yield is reported.time (
ndarray) – ndarray, shape (T,) Monotonic time grid for propagation (units consistent with Hamiltonians).B (
ndarray) – ndarray, shape (NB,) Magnetic-field grid for the sweep (scalar magnitude in the chosen axis).D (
float) – float Dipolar interaction strength used to buildH_D.J (
float) – float Exchange interaction strength used to buildH_J.kinetics (
list[HilbertIncoherentProcessBase]) – list[HilbertIncoherentProcessBase], optional Kinetic processes to apply during evolution (e.g., Haberkorn).relaxations (
list[HilbertIncoherentProcessBase]) – list[HilbertIncoherentProcessBase], optional Relaxation processes to apply during evolution (semiclassical variants).theta (
Optional[float]) – float, optional Polar angle (radians) of the magnetic-field axis relative to the lab frame. IfNone, the simulator default is used.phi (
Optional[float]) – float, optional Azimuthal angle (radians) of the magnetic-field axis. IfNone, the simulator default is used.num_samples (
Optional[int]) – int, optional Number of stochastic samples/trajectories for the semiclassical averaging. IfNone, the simulator default is used.c (
Optional[float]) – float, optional Normalisation/contrast parameter forwarded tomary_lfe_hfefor MARY post-processing.
- Return type:
- Returns:
- dict
A results dictionary with keys: -
time: ndarray, the input time grid. -B: ndarray, the field grid. -theta/phi: angles used for the sweep. -rhos: list/ndarray of density matrices (reshaped to square form). -time_evolutions: ndarray, product probabilities vs.timeandB. -product_yields: ndarray, integrated yields vs.B. -product_yield_sums: float or ndarray, sum. -MARY: ndarray, normalised magnetoresponse vs.B. -LFE: float, low-field effect (%). -HFE: float, high-field effect (%).
- radicalpy.experiments.modulated_mary_brute_force(Bs, modulation_depths, modulation_frequency, time_constant, harmonics, lfe_magnitude)[source]
Lock-in detected MARY via brute-force phase randomisation and numerical integration.
Source: Konowalczyk et al. Phys. Chem. Chem. Phys. 23, 1273-1284 (2021).
- Parameters:
Bs (np.ndarray) – Array of bias fields at which to compute the MARY signal (G).
modulation_depths (list) – List of field modulation amplitudes (G).
modulation_frequency (float) – Field modulation angular frequency (Hz).
time_constant (float) – Lock-in amplifier time constant used for exponential weighting of the reference (s).
harmonics (list) – Harmonic indices (e.g., [1,2]) for which to compute responses.
lfe_magnitude (float) – Amplitude parameter controlling the Lorentzian LFE line shape.
- Returns:
Tensor of shape
(len(harmonics), len(modulation_depths), len(Bs))containing RMS lock-in amplitudes at each harmonic, modulation depth, and bias field.- Return type:
np.ndarray
- radicalpy.experiments.nmr(multiplets, spectral_width, number_of_points, fft_number, transmitter_frequency, carrier_position, linewidth, scale=1.0)[source]
Simple 1D NMR spectrum synthesiser (FID → FFT) with multiplets and T2 decay.
- Parameters:
multiplets (list) – Each entry
[number of nuclei, chemical shift, multiplicity, scalar coupling].spectral_width (float) – Spectral width (Hz).
number_of_points (float) – Number of acquired points in the time domain.
fft_number (float) – Zero-filled length for the FFT (≥
number_of_points).transmitter_frequency (float) – Transmitter frequency (MHz).
carrier_position (float) – Carrier position (ppm) used to define the reference.
linewidth (float) – Lorentzian linewidth (Hz) → T2 = 1/(π·linewidth).
scale (float) – Overall multiplicative scale on the final spectrum.
- Returns:
ppm: Chemical shift axis (ppm).
spectrum: Complex spectrum after FFT (same length as
fft_number).
- Return type:
(np.ndarray, np.ndarray)
- radicalpy.experiments.odmr(sim, init_state, obs_state, time, D, J, B0, B1, B1_freq, B0_axis='z', B1_axis='x', kinetics=[], relaxations=[], hfc_anisotropy=False)[source]
Optically detected magnetic resonance (ODMR) simulation vs RF frequency.
- Parameters:
sim (HilbertSimulation) – Simulation object.
init_state (State) – Initial
Stateof the density matrix.obs_state (State) – Observable
Stateof the density matrix.time (np.ndarray) – Sequence of (uniform) time points (s).
D (float) – Dipolar coupling constant (mT).
J (float) – Exchange coupling constant (mT).
B0 (float) – Static magnetic field (mT) along
B0_axis.B1 (float) – RF/AC field amplitude (mT) along
B1_axis.B1_freq (np.ndarray) – RF angular frequency sweep (mT).
B0_axis (str) – Axis for
B0Zeeman term.B1_axis (str) – Axis for
B1Zeeman term.kinetics (list) – List of kinetic superoperators.
relaxations (list) – List of relaxation superoperators.
hfc_anisotropy (bool) – Include anisotropic hyperfine Hamiltonian if True.
- Returns:
time: original
timeB0: static field value
B0_axis: axis of
B0B1: RF amplitude
B1_axis: axis of
B1B1_freq: RF sweep values
B1_freq_axis: axis used for the RF sweep
rhos: density matrices (squared to Hilbert shape)
time_evolutions: product probabilities vs time & field
product_yields: integrated product yields
product_yield_sums: scalar yield per field
MARY: normalised magnetoresponse
LFE: low field effect (%)
HFE: high field effect (%)
- Return type:
- radicalpy.experiments.omfe(sim, init_state, obs_state, time, D, J, B1, B1_freq, B1_axis='x', B1_freq_axis='z', kinetics=[], relaxations=[], hfc_anisotropy=False)[source]
Oscillating magnetic field effect (OMFE) simulation vs RF frequency in transverse field.
- Parameters:
sim (HilbertSimulation) – Simulation object.
init_state (State) – Initial
Stateof the density matrix.obs_state (State) – Observable
Stateof the density matrix.time (np.ndarray) – Sequence of (uniform) time points (s).
D (float) – Dipolar coupling constant (mT).
J (float) – Exchange coupling constant (mT).
B1 (float) – Oscillating field amplitude (mT) along
B1_axis.B1_freq (np.ndarray) – Angular frequency sweep (mT) applied along
B1_freq_axis.B1_axis (str) – Axis for the static
B1Zeeman term.B1_freq_axis (str) – Axis used for the frequency-sweep effective term.
kinetics (list) – List of kinetic superoperators.
relaxations (list) – List of relaxation superoperators.
hfc_anisotropy (bool) – Include anisotropic hyperfine Hamiltonian if True.
- Returns:
time: original
timeB1: RF amplitude
B1_axis: axis of
B1B1_freq: RF sweep values
B1_freq_axis: axis used for the RF sweep
rhos: density matrices (squared to Hilbert shape)
time_evolutions: product probabilities vs time & field
product_yields: integrated product yields
product_yield_sums: scalar yield per field
MARY: normalised magnetoresponse
LFE: low field effect (%)
HFE: high field effect (%)
- Return type:
- radicalpy.experiments.oop_eseem(tau, J, D, T1=inf, n_quad=200)[source]
Out-of-phase-electron-spin echo envelope modulation (OOP-ESEEM) simulation. Computes S(tau) ∝ exp(-tau/T1) * ∫_0^π sin( 2 [ J - D (cos^2 θ - 1/3) ] tau ) sinθ dθ using Gauss–Legendre quadrature on u = cosθ ∈ [-1, 1].
- Parameters:
tau (float or np.ndarray) – Time (s).
J (float) – Exchange interaction (rad/s).
D (float) – Dipolar coupling constant (rad/s).
T1 (float) – Longitudinal relaxation time constant (s). Use np.inf to disable the exponential decay.
n_quad (int) – Number of Gauss–Legendre nodes (accuracy increases with n).
- Returns:
OOP-ESEEM spectrum.
- Return type:
S (np.ndarray)
- radicalpy.experiments.rydmr(sim, init_state, obs_state, time, D, J, B0, B1, B1_freq, B0_axis='z', B1_axis='x', kinetics=[], relaxations=[], hfc_anisotropy=False)[source]
Reaction yield-detected magnetic resonance (RYDMR) vs static field.
- Parameters:
sim (HilbertSimulation) – Simulation object.
init_state (State) – Initial
Stateof the density matrix.obs_state (State) – Observable
Stateof the density matrix.time (np.ndarray) – Sequence of (uniform) time points (s).
D (float) – Dipolar coupling constant (mT).
J (float) – Exchange coupling constant (mT).
B0 (np.ndarray) – Static field sweep (mT) along
B0_axis.B1 (float) – RF/AC amplitude (mT) along
B1_axis.B1_freq (float) – RF angular frequency offset (mT).
B0_axis (str) – Axis for
B0Zeeman term.B1_axis (str) – Axis for
B1Zeeman term.kinetics (list) – List of kinetic superoperators.
relaxations (list) – List of relaxation superoperators.
hfc_anisotropy (bool) – Include anisotropic hyperfine Hamiltonian if True.
- Returns:
time: original
timeB0: sweep values
B0_axis: axis of
B0B1: RF amplitude
B1_axis: axis of
B1B1_freq: RF angular frequency offset
B1_freq_axis: axis used for the offset term
rhos: density matrices (squared to Hilbert shape)
time_evolutions: product probabilities vs time & field
product_yields: integrated product yields
product_yield_sums: scalar yield per field
MARY: normalised magnetoresponse
LFE: low field effect (%)
HFE: high field effect (%)
- Return type:
- radicalpy.experiments.semiclassical_mary(sim, num_samples, init_state, ts, Bs, D, J, triplet_excited_state_quenching_rate, free_radical_escape_rate, kinetics, relaxations, scale_factor)[source]
Semiclassical MARY with stochastic averaging and explicit population channels.
Source: Maeda et al. Mol. Phys. 104, 1779–1788 (2006).
- Parameters:
sim (SemiclassicalSimulation) – Simulation object.
num_samples (int) – Number of stochastic hyperfine field realisations.
init_state (State) – Initial projection operator state.
ts (ArrayLike) – Time grid (s).
Bs (ArrayLike) – Magnetic field sweep (mT).
D (float) – Dipolar coupling constant (mT).
J (float) – Exchange coupling constant (mT).
triplet_excited_state_quenching_rate (float) – Quenching rate (1/s) feeding RP.
free_radical_escape_rate (float) – Escape rate from free radical (1/s).
kinetics (list[ArrayLike]) – Kinetic Liouvillian/superoperators.
relaxations (list[ArrayLike]) – Relaxation superoperators.
scale_factor (float) – Averaging weights for decay construction.
- Returns:
ts: time grid
Bs: field sweep
MARY: response matrix with shape
(len(ts), len(Bs))
- Return type:
- radicalpy.experiments.steady_state_mary(sim, init, obs, Bs, D, E, J, theta, phi, kinetics=[])[source]
Steady-state MARY via linear solve of Liouvillian with ZFS, exchange, and Zeeman terms.
- Parameters:
sim (LiouvilleSimulation) – Simulation object.
obs (State) – Observable
Stateused to form the projection operatorQ.Bs (np.ndarray) – Magnetic field sweep (mT).
D (float) – Zero-field splitting parameter D (mT).
E (float) – Zero-field splitting parameter E (mT).
J (float) – Exchange coupling (mT).
theta (float) – Polar angle of the Zeeman field.
phi (float) – Azimuthal angle of the Zeeman field.
kinetics (list) – List of kinetic superoperators applied to the Liouvillian.
- Returns:
rhos: Steady-state density vectors, one per field (shape
(len(Bs), N)).Phi_s: Projection
rhos @ Q.flatten()giving steady-state signal per field.
- Return type:
np.ndarray
radicalpy.kinetics module
Kinetics operators and superoperators for radical-pair dynamics.
This module implements common incoherent kinetic processes acting on spin systems in either Hilbert space (operators) or Liouville space (superoperators). These terms model recombination, product formation, and phenomenological decay channels that act alongside coherent spin evolution.
- Classes:
HilbertKineticsBase: Base class for Hilbert-space kinetics operators.LiouvilleKineticsBase: Base class for Liouville-space kinetics superoperators.Exponential: Simple exponential decay of product probabilities in Hilbert space (Kaptein model).Haberkorn: Haberkorn singlet/triplet selective recombination superoperator.HaberkornFree: Haberkorn-style uniform decay (free radical / RP2 formation).JonesHore: Jones–Hore two-site kinetics with separate S/T rates.
- Usage pattern:
Instantiate a kinetics object with the desired rate parameters (e.g.,
rate_constant,singlet_rate,triplet_rate,target).Call
init(sim)with a simulation exposing the required API (projection operators, sizes, and Liouville conversion).Combine the returned term with your total generator: - Hilbert space: use the operator’s effect where appropriate. - Liouville space: add
subHto the Liouvillian.
- Args conventions (per class):
Exponential(rate_constant): Scales product probabilities asexp(-rate * t).Haberkorn(rate_constant, target): Selective S/T recombination wheretargetis one ofState.SINGLET,State.TRIPLET, or specific triplet substates supported by the code.HaberkornFree(rate_constant): Uniform (state-independent) decay proportional to identity in Liouville space.JonesHore(singlet_rate, triplet_rate): Two-channel model with separate S and T rates and a coupling term constructed from S/T projectors.
Notes
Hilbert vs Liouville: Hilbert-space
Exponentialmodifies product probabilities directly; Liouville-space classes construct superoperators (subH) via Kronecker products and projector conversions.Projectors:
QS,QT, and triplet sublevel projectors are obtained from the simulation (sim.projection_operator(...)); Haberkorn and Jones–Hore rely on these for state selectivity.Rates and units: All rate parameters are in s⁻¹. Ensure time arrays passed to
Exponential.adjust_product_probabilitiesare in seconds.
References
[Haberkorn, Mol. Phys. 32 (5), 1491–1493 (1976)](http://dx.doi.org/10.1080/00268977600102851).
[Kaptein et al., Chem. Phys. Lett. 4 (4), 195–197 (1969)](https://doi.org/10.1016/0009-2614(69)80098-9).
[Jones et al., Chem. Phys. Lett. 507, 269–273 (2011)](https://doi.org/10.1016/j.cplett.2011.03.082).
- Requirements:
numpyfor array operations.A simulation object with:
projection_operator(State.*),hamiltonian_size, and a Liouville conversion method (e.g.,sim._convert(Q)).
- raises ValueError:
Haberkornvalidates thattargetis singlet or triplet (or supported triplet sublevels) and will raise if unsupported.
See also
radicalpy.simulation.HilbertIncoherentProcessBase,
radicalpy.simulation.LiouvilleIncoherentProcessBase,
and related relaxation modules for non-kinetic incoherent processes.
- class radicalpy.kinetics.Exponential(rate_constant)[source]
Bases:
HilbertKineticsBaseExponential model kinetics operator.
Source: Kaptein et al. Chem. Phys. Lett. 4, 4, 195-197 (1969).
>>> Exponential(rate_constant=1e6) Kinetics: Exponential Rate constant: 1000000.0
- adjust_product_probabilities(product_probabilities, time)[source]
See
radicalpy.simulation.HilbertIncoherentProcessBase.adjust_product_probabilities.
- class radicalpy.kinetics.Haberkorn(rate_constant, target)[source]
Bases:
LiouvilleKineticsBaseHaberkorn kinetics superoperator for singlet/triplet recombination/product formation.
Source: Haberkorn, Mol. Phys. 32:5, 1491-1493 (1976).
- Parameters:
>>> Haberkorn(rate_constant=1e6, target=State.SINGLET) Kinetics: Haberkorn Rate constant: 1000000.0 Target: S
>>> Haberkorn(rate_constant=1e6, target=State.TRIPLET) Kinetics: Haberkorn Rate constant: 1000000.0 Target: T
- class radicalpy.kinetics.HaberkornFree(rate_constant)[source]
Bases:
LiouvilleKineticsBaseHaberkorn kinetics superoperator for free radical/RP2 formation.
Source: Haberkorn, Mol. Phys. 32:5, 1491-1493 (1976).
>>> HaberkornFree(rate_constant=1e6) Kinetics: HaberkornFree Rate constant: 1000000.0
- class radicalpy.kinetics.HilbertKineticsBase(rate_constant)[source]
Bases:
HilbertIncoherentProcessBaseBase class for kinetics operators (Hilbert space).
- class radicalpy.kinetics.JonesHore(singlet_rate, triplet_rate)[source]
Bases:
LiouvilleKineticsBaseJones-Hore kinetics superoperator for two-site models.
Source: Jones et al. Chem. Phys. Lett. 507, 269-273 (2011).
- Parameters:
>>> JonesHore(1e7, 1e6) Kinetics: JonesHore Singlet rate: 10000000.0 Triplet rate: 1000000.0
- class radicalpy.kinetics.LiouvilleKineticsBase(rate_constant)[source]
Bases:
LiouvilleIncoherentProcessBaseBase class for kinetics superoperators (Liouville space).
radicalpy.plot module
Plotting utilities for spin dynamics, tensors, and Monte Carlo trajectories.
This module collects convenience routines for visualising common objects in spin chemistry and molecular simulations, including: energy-level diagrams, anisotropy/tensor surfaces on the unit sphere, 3D random-walk trajectories (free and caged), density-matrix bar animations, and general helper plots for molecules and time–series.
Functions in this module build on NumPy and Matplotlib’s 3D toolkit and
are intended for exploratory analysis and figure generation inside
notebooks or scripts. Most functions draw directly to the active
Matplotlib figure and return None, except where noted.
Contents:
anisotropy_surface: 3D surface whose radius/color encodes Re(Y(θ,φ)).
density_matrix_animation: Animated 3D bar plot of \(\lvert\rho\rvert\) over time.
linear_energy_levels: Event-plot (vertical lines) of Hamiltonian eigenvalues.
energy_levels: Eigen-energies vs magnetic field for aHilbertSimulation.
monte_carlo_free: 3D trajectory of a free random walk (nm scaling).
monte_carlo_caged: 3D trajectory within a spherical cage of radiusr_max.
plot_3d_results: Colored 3D surface z(x, y) with adjustable camera.
plot_autocorrelation_fit: Autocorrelation (log-x) with fitted curve.
plot_bhalf_time: Discrete B_{1/2} estimates vs time with error bars.
plot_exchange_interaction_in_solution: Separation and exchange (twin y-axes).
plot_general: Simple x–y line plot with labels/styles.
plot_molecule: Minimal 3D stick model from atom coords and bonds.
set_equal_aspect: Equalizes 3D axis scales to data range.
spin_state_labels: LaTeX-formatted ket labels for radical pairs.
_format_label: Helper that wraps text in a Dirac ket for LaTeX.
visualise_tensor: Rank-2 tensor surface on the sphere after rotation/shift.
Notes
Many plotting functions assume Matplotlib 3D axes; some create and manage figures/axes internally. To integrate into existing layouts, adapt the code to accept/use a provided
Axes3Dwhere appropriate.Units are noted in axis labels (e.g., nm, mT, s). Some helpers apply scaling factors (e.g., 1e9) for visualization; inspect the source if absolute coordinates are required.
energy_levelsassumes aHilbertSimulationAPI exposingtotal_hamiltonianandzeeman_hamiltonian. Replace or wrap as needed.
Dependencies:
numpyfor array operations.
matplotlib(includingmpl_toolkits.mplot3d,cm, andcolors) for plotting.
matplotlib.animation.FuncAnimationfor animated density-matrix plots.
- raises ValueError:
spin_state_labelsraises ifsimdoes not contain exactly two radicals (labels are defined for radical pairs).
See also
matplotlib.pyplot, mpl_toolkits.mplot3d, and
domain-specific simulation objects (e.g., HilbertSimulation)
used by the energy-level routines.
- radicalpy.plot.anisotropy_surface(theta, phi, Y)[source]
Anisotropy surface plot.
Renders a 3D surface on the unit sphere whose radius is proportional to the real part of a field
Y(θ,φ), and colors the surface byRe(Y).- Parameters:
theta (np.ndarray) – Polar angles θ in radians (shape: (N,) or mesh-ready).
phi (np.ndarray) – Azimuthal angles φ in radians (shape: (M,) or mesh-ready).
Y (np.ndarray) – Complex field values sampled on (θ, φ); only the real component is used for coloring and scaling.
- Returns:
Displays a Matplotlib 3D surface plot.
- Return type:
None
- radicalpy.plot.density_matrix_animation(rhos, frames, bar3d_kwargs, axes_kwargs)[source]
Density matrix bar-plot animation.
Builds a 3D bar plot over time from a sequence of density matrices, coloring bars by magnitude and returning a
FuncAnimation.- Parameters:
- Returns:
The configured animation object.
- Return type:
matplotlib.animation.FuncAnimation
- radicalpy.plot.energy_levels(sim, B, J=0, D=0)[source]
Energy levels vs magnetic field.
Computes eigen-energies of the total Hamiltonian
H = H_base + B0 * H_zeefor each applied field inBand plots levels as functions ofB0.- Parameters:
sim (HilbertSimulation) – Simulation in Hilbert space.
B (np.ndarray) – Magnetic field values (T).
J (float) – Isotropic exchange parameter (default: 0).
D (float) – Dipolar coupling parameter (default: 0).
- Returns:
Displays a Matplotlib line plot of energy levels vs field.
- Return type:
None
- radicalpy.plot.linear_energy_levels(H, B, linecolour, title)[source]
Linear spectrum event plot.
Diagonalizes a Hamiltonian and shows its eigenvalues as vertical events, useful for compact level-spectrum visualization.
- radicalpy.plot.monte_carlo_caged(pos, r_max)[source]
3D random-walk trajectory (caged).
Plots a Monte Carlo trajectory inside a spherical cage of radius
r_max, rendering the wireframe boundary and the particle path.- Parameters:
pos (np.ndarray) – Cartesian positions along a trajectory (shape: (T, 3), in metres).
r_max (float) – Cage radius (metres) for the confining sphere.
- Returns:
Displays a 3D wireframe and trajectory plot.
- Return type:
None
- radicalpy.plot.monte_carlo_free(pos)[source]
3D random-walk trajectory (free solution).
Plots a radical-pair Monte Carlo path in free solution, scaling coordinates to nanometres for visualization and marking start and origin.
- Parameters:
pos (np.ndarray) – Cartesian positions along a trajectory (shape: (T, 3), in metres).
- Returns:
Displays a 3D line plot of the walk.
- Return type:
None
- radicalpy.plot.plot_3d_results(xdata, ydata, zdata, xlabel, ylabel, zlabel, azim=-135, dist=10, elev=35, factor=1000000.0)[source]
3D surface plot of results.
Creates a colored surface
z(x,y)with adjustable 3D view parameters and axis labels—useful for field maps or parameter sweeps.- Parameters:
xdata (np.ndarray) – X-axis coordinates.
ydata (np.ndarray) – Y-axis coordinates.
zdata (np.ndarray) – Z values on the meshgrid of (x, y).
xlabel (str) – Label for the X axis.
ylabel (str) – Label for the Y axis.
zlabel (str) – Label for the Z axis.
azim (float) – Azimuthal angle of the 3D view (deg).
dist (float) – Camera distance parameter.
elev (float) – Elevation angle of the 3D view (deg).
factor (float) – Multiplier applied to Y for unit scaling.
- Returns:
Displays a Matplotlib 3D surface plot.
- Return type:
None
- radicalpy.plot.plot_autocorrelation_fit(t_j, acf_j, acf_j_fit, zero_point_crossing_j)[source]
Autocorrelation with fit (log-scale).
Plots an autocorrelation function up to its first zero crossing together with a fitted curve provided in
acf_j_fit["fit"].- Parameters:
- Returns:
Displays a Matplotlib line plot on a log-x axis.
- Return type:
None
- radicalpy.plot.plot_bhalf_time(ts, bhalf_time, fit_error_time, style='ro', factor=1000000.0)[source]
B_{1/2}-field width vs time.Plots discrete estimates of the half-field width
B_{1/2}against time with error bars from fitting uncertainties.- Parameters:
ts (np.ndarray) – Time points (s).
bhalf_time (np.ndarray) – Estimated
B_{1/2}(mT) per time point.fit_error_time (np.ndarray) – Fit errors (e.g., covariance-derived) where index [1, i] is used as y-error.
style (str) – Matplotlib line/marker style (default: “ro”).
factor (float) – Multiplier applied to x-values for unit scaling.
- Returns:
Displays a scatter plot with error bars.
- Return type:
None
- radicalpy.plot.plot_exchange_interaction_in_solution(ts, trajectory_data, j)[source]
Exchange interaction and separation vs time.
Plots radical-pair separation and corresponding exchange interaction on twin y-axes against time.
- Parameters:
ts (np.ndarray) – Time points (s or ns as labeled).
trajectory_data (np.ndarray) – Trajectory array where column 1 contains separations (m).
j (np.ndarray) – Exchange interaction values (mT), same length as
ts.
- Returns:
Displays a Matplotlib plot with twin y-axes.
- Return type:
None
- radicalpy.plot.plot_general(xdata, ydata, xlabel, ylabel, style='-', label=[], colors='r', factor=1)[source]
General line plot.
Convenience wrapper for a simple x–y line plot with label, style, color, and optional scaling of the x-values.
- Parameters:
- Returns:
Displays a Matplotlib line plot.
- Return type:
None
- radicalpy.plot.plot_molecule(ax, labels, elements, coords, bonds, show_labels=True, show_atoms=False)[source]
Molecular stick model.
Draws a simple 3D molecular diagram from atom coordinates and bond pairs, optionally showing atom spheres and/or atom labels.
- Parameters:
ax (mpl_toolkits.mplot3d.Axes3D) – Target 3D axes.
labels (Sequence[str]) – Per-atom labels (e.g., element symbols).
elements (Sequence[str]) – Per-atom element identifiers.
coords (np.ndarray) – Cartesian coordinates (Å or nm) of shape (N, 3).
bonds (Sequence[Tuple[int,int]]) – Index pairs of bonded atoms.
show_labels (bool) – Whether to render atom labels.
show_atoms (bool) – Whether to render atom scatter points.
- Returns:
Draws onto the provided axes.
- Return type:
None
- radicalpy.plot.plot_sphere(ax, radius=1, color='black', alpha=0.05)[source]
Plot a wire-framed, lightly shaded sphere on a Matplotlib 3D axis.
The surface is rendered via
Axes3D.plot_surfaceusing a spherical parameterisation, and three faint coordinate axes (x, y, z) are overlaid through the sphere’s center for orientation. A thin great-circle outline is also drawn.- Parameters:
ax – matplotlib.axes._subplots.Axes3DSubplot or mpl_toolkits.mplot3d.Axes3D A 3D Matplotlib axes object on which to draw.
radius – float, optional Sphere radius. Default is
1.color – str or tuple, optional Base color for the sphere surface and outlines. Default is
'black'.alpha – float, optional Surface transparency in
[0, 1]. Default is0.05.
- Returns:
- None
The function draws on
axin place and returnsNone.
- radicalpy.plot.set_equal_aspect(ax, X, Y, Z)[source]
Equalise 3D aspect by data range.
Sets axis limits so the plotted 3D data have equal scale along each axis.
- Parameters:
ax (mpl_toolkits.mplot3d.Axes3D) – Target 3D axes.
X (np.ndarray) – X coordinates.
Y (np.ndarray) – Y coordinates.
Z (np.ndarray) – Z coordinates.
- Returns:
Adjusts axis limits in place.
- Return type:
None
- radicalpy.plot.spin_state_labels(sim)[source]
Spin-state labels for a radical pair.
Generates human-readable kets for singlet/triplet sublevels combined with radical spin projections, suitable for plot tick labels.
- Parameters:
sim (HilbertSimulation) – Simulation containing exactly two radicals.
- Returns:
LaTeX-formatted labels like
"$\vert T_+, +1/2 \rangle$".- Return type:
- radicalpy.plot.visualise_tensor(ax, tensor, rot_matrix, coords, colour)[source]
Rank-2 tensor visualisation on a sphere.
Projects a symmetric rank-2 tensor onto directions on the unit sphere, scales radius by the directional quadratic form, and plots the resulting surface in 3D after applying a rotation and translation.
- Parameters:
ax (mpl_toolkits.mplot3d.Axes3D) – Target 3D axes.
tensor (np.ndarray) – 3×3 tensor (e.g., hyperfine or g-tensor).
rot_matrix (np.ndarray) – 3×3 rotation matrix to orient the tensor.
coords (np.ndarray) – 3-vector translation applied to the surface.
colour (Any) – Matplotlib color spec for the surface.
- Returns:
The rendered surface coordinates with shape (Nθ, Nφ, 3).
- Return type:
np.ndarray
radicalpy.relaxation module
Relaxation superoperators in Liouville space for radical-pair dynamics.
This module defines a family of incoherent (relaxational) processes as
Liouville-space superoperators that act on vectorised density matrices.
Each process contributes an additive term subH (a superoperator) that
can be combined with coherent Liouvillians to evolve spin systems subject
to stochastic environmental interactions.
The implementations follow standard models in spin chemistry and magnetic resonance, including singlet–triplet dephasing, random local fields, triplet–triplet relaxation/dephasing, T₁/T₂ Bloch-type relaxation, and anisotropic g-tensor modulation under rotational diffusion.
- Classes:
LiouvilleRelaxationBase: Common base for relaxation superoperators.DipolarModulation: Dipolar modulation model (Kattnig et al., 2016).GTensorAnisotropy: g-tensor anisotropy relaxation (Kivelson, 1960).RandomFields: Isotropic random-field model (Kattnig et al., 2016).SingletTripletDephasing: S–T dephasing channel (Shushin, 1991).T1Relaxation: Longitudinal (spin–lattice) relaxation (Bloch, 1946).T2Relaxation: Transverse (spin–spin) relaxation (Bloch, 1946).TripletTripletDephasing: Triplet–triplet dephasing (Gorelik et al., 2001).TripletTripletRelaxation: Triplet–triplet population relaxation (Miura et al., 2015).
- Helper functions:
_g_tensor_anisotropy_term: Per-radical superoperator contribution for g-anisotropy with Kivelson spectral densities.
- Key concepts:
Liouville space / superoperators: Operators acting on vectorized density matrices. Additive relaxation terms are represented by
subH.Projection operators:
Q_S,Q_T,Q_{T+},Q_{T0},Q_{T-}select singlet/triplet subspaces and enter several models.Spin operators: Single-spin
S_x, S_y, S_zper radical index, used to build isotropic/anisotropic relaxation channels via Kronecker products.Spectral density: Frequency- and correlation-time–dependent scaling (e.g., Kivelson form) for stochastic modulation processes.
- Usage pattern:
Instantiate a relaxation class with its parameters (e.g.,
rate_constant,tau_c,gprincipal values,omega).Call
init(sim)with aLiouvilleSimulationprovidingprojection_operator(...)andspin_operator(radical_idx, axis).Retrieve the superoperator via the instance’s
subHattribute and add it to your total Liouvillian.
- Args conventions (per-class):
rate_constant(float, s⁻¹): Overall relaxation rate used in most models.g1,g2(list[float]): Principal components of the radicals’ g-tensors.omega1,omega2(float, rad·s⁻¹·mT⁻¹ × B): Larmor prefactors; the actual frequency is typicallyomega * B0. See note below.tau_c1,tau_c2(float, s): Rotational correlation times.
Notes
Magnetic-field dependence: For
GTensorAnisotropy, the instantaneous Larmor frequency is proportional to the applied fieldB0. IfB0varies (e.g., in MARY scans), computeomegadynamically fromB0rather than treating it as a fixed constant.Vectorisation size: Superoperators are built with Kronecker products sized to the underlying Hilbert spaces of the radicals in
sim.Normalisation and units: Rate-like parameters are in s⁻¹ unless noted. Axis labels/units are not handled here (these are dynamical, not plotting utilities).
References
[Bloch, Phys. Rev. 70, 460–474 (1946)](https://doi.org/10.1103/PhysRev.70.460).
[Gorelik et al., J. Phys. Chem. A 105, 8011–8017 (2001)](https://doi.org/10.1021/jp0109628).
[Kattnig et al., New J. Phys. 18, 063007 (2016)](https://iopscience.iop.org/article/10.1088/1367-2630/18/6/063007).
[Kivelson, J. Chem. Phys. 33, 1094 (1960)](https://doi.org/10.1063/1.1731340).
[Miura et al., J. Phys. Chem. A 119, 5534–5544 (2015)](https://doi.org/10.1021/acs.jpca.5b02183).
[Shushin, Chem. Phys. Lett. 181 (2–3), 274–278 (1991)](https://doi.org/10.1016/0009-2614(91)90366-H).
- Requirements:
numpyfor array/Kronecker algebra.A simulation object exposing:
projection_operator(State.*)andspin_operator(idx, 'x'|'y'|'z'), as used in theinitmethods.
- raises ValueError:
May be raised upstream if the simulation object does not support the required operators or radical indexing.
See also
radicalpy.simulation.LiouvilleSimulation,
radicalpy.states.State,
and the corresponding coherent Liouvillian construction in your codebase.
- class radicalpy.relaxation.DipolarModulation(rate_constant)[source]
Bases:
LiouvilleRelaxationBaseDipolar modulation relaxation superoperator.
Source: Kattnig et al. New J. Phys., 18, 063007 (2016).
>>> DipolarModulation(rate_constant=1e6) Relaxation: DipolarModulation Rate constant: 1000000.0
- class radicalpy.relaxation.GTensorAnisotropy(g1, g2, omega1, omega2, tau_c1, tau_c2)[source]
Bases:
LiouvilleRelaxationBaseg-tensor anisotropy relaxation superoperator.
Source: Kivelson, J. Chem. Phys. 33, 1094 (1960).
- Parameters:
g1 (list) – The principle components of g-tensor of the first radical.
g2 (list) – The principle components of g-tensor of the second radical.
omega1 (float) – The Larmor frequency of the first radical (rad/s/mT).
omega2 (float) – The Larmor frequency of the second radical (rad/s/mT).
tau_c1 (float) – The rotational correlation time of the first radical (s).
tau_c2 (float) – The rotational correlation time of the second radical (s).
>>> GTensorAnisotropy(g1=[2.0032, 1.9975, 2.0014], ... g2=[2.00429, 2.00389, 2.00216], ... omega1=-158477366720.7, ... omega2=-158477366720.7, ... tau_c1=5e-12, ... tau_c2=100e-12) Relaxation: GTensorAnisotropy g1: [2.0032, 1.9975, 2.0014] g2: [2.00429, 2.00389, 2.00216] omega1: -158477366720.7 omega2: -158477366720.7 tau_c1: 5e-12 tau_c2: 1e-10
- class radicalpy.relaxation.LiouvilleRelaxationBase(rate_constant)[source]
Bases:
LiouvilleIncoherentProcessBaseBase class for relaxation superoperators (Liouville space).
- class radicalpy.relaxation.RandomFields(rate_constant)[source]
Bases:
LiouvilleRelaxationBaseRandom fields relaxation superoperator.
Source: Kattnig et al. New J. Phys., 18, 063007 (2016).
>>> RandomFields(rate_constant=1e6) Relaxation: RandomFields Rate constant: 1000000.0
- class radicalpy.relaxation.SingletTripletDephasing(rate_constant)[source]
Bases:
LiouvilleRelaxationBaseSinglet-triplet dephasing relaxation superoperator.
Source: Shushin, Chem. Phys. Lett. 181, 2,3, 274-278 (1991).
>>> SingletTripletDephasing(rate_constant=1e6) Relaxation: SingletTripletDephasing Rate constant: 1000000.0
- class radicalpy.relaxation.T1Relaxation(rate_constant)[source]
Bases:
LiouvilleRelaxationBaseT1 (spin-lattice, longitudinal, thermal) relaxation superoperator.
Source: Bloch, Phys. Rev. 70, 460-474 (1946).
>>> T1Relaxation(rate_constant=1e6) Relaxation: T1Relaxation Rate constant: 1000000.0
- class radicalpy.relaxation.T2Relaxation(rate_constant)[source]
Bases:
LiouvilleRelaxationBaseT2 (spin-spin, transverse) relaxation superoperator.
Source: Bloch, Phys. Rev. 70, 460-474 (1946).
>>> T2Relaxation(rate_constant=1e6) Relaxation: T2Relaxation Rate constant: 1000000.0
- class radicalpy.relaxation.TripletTripletDephasing(rate_constant)[source]
Bases:
LiouvilleRelaxationBaseTriplet-triplet dephasing relaxation superoperator.
Source: Gorelik et al. J. Phys. Chem. A 105, 8011-8017 (2001).
>>> TripletTripletDephasing(rate_constant=1e6) Relaxation: TripletTripletDephasing Rate constant: 1000000.0
- class radicalpy.relaxation.TripletTripletRelaxation(rate_constant)[source]
Bases:
LiouvilleRelaxationBaseTriplet-triplet relaxation superoperator.
Source: Miura et al. J. Phys. Chem. A 119, 5534-5544 (2015).
>>> TripletTripletRelaxation(rate_constant=1e6) Relaxation: TripletTripletRelaxation Rate constant: 1000000.0
radicalpy.simulation module
Simulation utilities for spin dynamics.
This module provides classes and helpers to build quantum mechanical models in either Hilbert space (density matrices as square arrays) or Liouville space (vectorised densities / superoperators). It focuses on electron–nuclear spin systems typical of radical pairs and triplet pairs and supports common interactions and observables used in magnetic resonance and spin chemistry.
Main classes:
State:Enumerates common initial/observable spin states (singlet/triplet manifold, EPR observable, thermal equilibrium, etc.).
Basis:Choice of electron-pair basis: Zeeman or singlet–triplet (S/T).
HilbertSimulation:Core simulator that assembles Hamiltonians (Zeeman, hyperfine, exchange, dipolar, optional zero-field splitting), prepares initial density matrices, propagates them unitarily, and evaluates product probabilities/yields.
LiouvilleSimulation:Extends
HilbertSimulationwith Liouville-space evolution (superoperators, vectorised densities) for convenient inclusion of incoherent processes.
HilbertIncoherentProcessBase/LiouvilleIncoherentProcessBase:Base hooks to augment Hamiltonians or measured probabilities with phenomenological kinetics / relaxation.
SparseCholeskyHilbertSimulation:Hilbert-space variant optimised for large systems via sparse algebra and a Cholesky-factor time-stepping scheme.
SemiclassicalSimulation:Generates random semiclassical Hamiltonians for ensemble treatments.
Key features:
Spin operators for arbitrary particles and bases.
Zeeman (1D/3D), hyperfine (isotropic/tensor), exchange (J), dipolar (1D/3D),
and ZFS Hamiltonians. - Initial states: projection-based (S, T, etc.) and thermal equilibrium. - Time evolution: Hilbert (ρ → U ρ U†) and Liouville (ρ → e^{Lt} ρ). - Observables: projection operators and product probabilities/yields. - Shape conventions:
Hilbert density:
(dim, dim)Liouville density (vectorised):
(dim**2, 1)
Units & conventions:
Magnetic fields in mT; gyromagnetic ratios provided as
gamma_mT.Tensors follow x/y/z Cartesian ordering.
S/T transform from the Zeeman basis.
See also
utils.spherical_to_cartesianfor field orientation.Module docstrings of related packages/classes for data structures (
Molecule,
nuclei, radicals, hyperfine data).
- class radicalpy.simulation.Basis(value)[source]
Bases:
EnumSpin basis choices for electron pair subspace (Zeeman vs. singlet–triplet).
- ST = 'ST'
- ZEEMAN = 'Zeeman'
- class radicalpy.simulation.HilbertIncoherentProcessBase(rate_constant)[source]
Bases:
object- adjust_hamiltonian(*args, **kwargs)[source]
Optionally modify the Hamiltonian in-place.
Subclasses can implement e.g. Redfield/Lindblad additions. The default implementation is a no-op and returns
None.
- adjust_product_probabilities(*args, **kwargs)[source]
Optionally post-process simulated product probabilities.
Can be used to fold in kinetic schemes in Hilbert simulations. The default implementation is a no-op and returns
None.
- class radicalpy.simulation.HilbertSimulation(molecules, custom_gfactors=False, basis=Basis.ST)[source]
Bases:
objectQuantum simulation base class.
- Parameters:
>>> HilbertSimulation([Molecule.fromdb("flavin_anion", ["N5"]), ... Molecule.fromdb("tryptophan_cation", ["Hbeta1", "H1"])]) Number of electrons: 2 Number of nuclei: 3 Number of particles: 5 Multiplicities: [2, 2, 3, 2, 2] Magnetogyric ratios (mT): [-176085963.023, -176085963.023, 19337.792, 267522.18744, 267522.18744] Nuclei: [14N(19337792.0, 3, 0.5141 <anisotropic available>), 1H(267522187.44, 2, 1.605 <anisotropic available>), 1H(267522187.44, 2, -0.5983 <anisotropic available>)] Couplings: [0, 1, 1] HFCs (mT): [0.5141 <anisotropic available>, 1.605 <anisotropic available>, -0.5983 <anisotropic available>]
- ST_basis(M)[source]
Transform an operator from the Zeeman basis to the S/T basis.
- Parameters:
M – Operator in the Zeeman basis.
- Returns:
The operator expressed in the S/T basis.
- static apply_hilbert_kinetics(time, product_probabilities, kinetics)[source]
Apply kinetic post-processing to product probabilities in Hilbert space.
Each object in
kineticsmay rescale or filter the probabilities as a function oftime(e.g., recombination channels).
- apply_liouville_hamiltonian_modifiers(H, modifiers)[source]
Apply (Hilbert) incoherent process modifiers to the Liouville Hamiltonian.
Each modifier is initialised with
selfonce, then given the chance to adjustHin place. This is a no-op for Hilbert simulations.
- bloch_redfield_liouvillian(H, bath, noise, secular=True)[source]
Bloch-Redfield tensor builder.
- Parameters:
- Returns:
Bloch-Redfield Liouvillian in the energy basis. eigvecs (np.ndarray): Columns are eigenkets of H.
- Return type:
L (np.ndarray)
- bloch_redfield_solver(L_e, eigvecs, rho0, time, obs=None, rtol=1e-07, atol=1e-07)[source]
Evolve Bloch-Redfield master equation in the energy basis.
- Parameters:
L_e (np.ndarray) – Liouvillian in energy basis.
eigvecs (np.ndarray) – Energy eigenvectors.
rho0 (np.ndarray) – Initial density matrix in lab basis.
time (np.ndarray) – Time in seconds.
obs (list[np.ndarray]) – Expectation operator in lab basis.
- Returns:
{‘states’: […]} or {‘expect’: [arrays…]}
- Return type:
results (dict)
- bloch_redfield_time_evolution(H, rho0, time, bath, noise=None, obs=None, secular=True, **kwargs)[source]
Bloch-Redfield Liouvillian time evolution solver.
- static convert(H)[source]
Convert a Hilbert-space Hamiltonian to the simulator’s working space.
In Hilbert simulations this is the identity; Liouville overrides it.
- Return type:
- property coupling
List mapping each nucleus to the index of its parent radical.
- dipolar_hamiltonian(D)[source]
Construct the Dipolar Hamiltonian.
Construct the Dipolar Hamiltonian based on dipolar coupling constant or dipolar interaction tensor
Dbetween two electrons. Depending on thetypeofD, the 1D or the 3D version is invoked.See:
- Parameters:
D (float | np.ndarray) – dipolar coupling constant or dipolar interaction tensor.
- Returns:
The Dipolar Hamiltonian corresponding to the system described by the
HilbertSimulationobject and dipolar coupling constant or dipolar interaction tensorD.- Return type:
np.ndarray
- dipolar_hamiltonian_1d(D)[source]
Construct the 1D Dipolar Hamiltonian.
Construct the Dipolar Hamiltonian based on dipolar coupling constant
Dbetween two electrons.The dipolar coupling constant can be obtained using
radicalpy.estimations.dipolar_interaction_isotropic.- Parameters:
D (float) – dipolar coupling constant in mT.
- Returns:
The 1D Dipolar Hamiltonian corresponding to the system described by the
HilbertSimulationobject and dipolar coupling constantD.- Return type:
np.ndarray
- dipolar_hamiltonian_3d(dipolar_tensor)[source]
Construct the 3D Dipolar Hamiltonian.
Construct the Dipolar Hamiltonian based on dipolar interaction tensor
Dbetween two electrons.The dipolar coupling tensor can be obtained using
radicalpy.estimations.dipolar_interaction_anisotropic.- Parameters:
dipolar_tensor (np.ndarray) – dipolar interaction tensor in mT.
- Returns:
The 3D Dipolar Hamiltonian corresponding to the system described by the
HilbertSimulationobject and dipolar interaction tensorD.- Return type:
np.ndarray
- exchange_hamiltonian(J, prod_coeff=2)[source]
Construct the exchange Hamiltonian.
Construct the exchange (J-coupling) Hamiltonian based on the coupling constant J between two electrons.
The J-coupling constant can be obtained using:
- Parameters:
- Returns:
The exchange (J-coupling) Hamiltonian corresponding to the system described by the
HilbertSimulationobject and the coupling constantJ.- Return type:
np.ndarray
- property hamiltonian_size
Dimension of the Hilbert space (product of particle multiplicities).
- hyperfine_hamiltonian(hfc_anisotropy=False)[source]
Construct the Hyperfine Hamiltonian.
Construct the Hyperfine Hamiltonian. If
hfc_anisotropyisFalse, then the isotropic hyperfine coupling constants are used. Ifhfc_anisotropyisTruethen the full hyperfine tensors are used (assuming they are available for all the nuclei of the molecule in the database, otherwise an exception is raised).- Parameters:
hfc_anisotropy (bool) – Use isotropic hyperfine coupling constants if
False, use full hyperfine tensors ifFalse.- Returns:
The Hyperfine Hamiltonian corresponding to the system described by the
HilbertSimulationobject.- Return type:
np.ndarray
- initial_density_matrix(state, H)[source]
Construct the initial density matrix for time evolution.
Builds a properly normalised density operator in Hilbert space corresponding to the chosen initial
state.For most states (e.g. singlet, triplet), this is simply the normalised projection operator.
For the special case
State.EQUILIBRIUM, the density is taken as the thermal equilibrium state, approximated via \(\rho_0 \propto e^{-i H \beta}\) where \(\beta = \hbar / (k_B T)\) is provided viaprojection_operator.
- Parameters:
state (State) – Target spin state (e.g.,
SINGLET,TRIPLET,EQUILIBRIUM).H (np.ndarray) – Spin Hamiltonian in Hilbert space.
- Returns:
Normalised density matrix in Hilbert space.
- Return type:
np.ndarray
- lindblad_liouvillian(H, Ls=[])[source]
Assemble the Liouville-space generator for coherent + Lindblad dynamics.
This builds the superoperator \(\mathcal{L} = -i\,[H,\cdot] + \sum_k \mathcal{D}[L_k]\) using the column–stacking (vec) convention. The coherent part is implemented as
\[-i[H,\rho] \;\mapsto\; -i\,(I\otimes H - H^{\mathsf{T}}\otimes I),\]and each dissipator is
\[\mathcal{D}[L](\rho) = L\,\rho\,L^{\dagger} - \tfrac{1}{2}\{L^{\dagger}L,\rho\} \;\mapsto\; L^{\ast}\otimes L - \tfrac{1}{2}\Big( I\otimes L^{\dagger}L + (L^{\mathsf{T}}L^{\ast})\otimes I \Big).\]- Parameters:
H (
ndarray) – Hilbert-space Hamiltonian. For Hermitian problems, a complex dtype (e.g.np.complex128) is recommended.Ls – Iterable of collapse operators
L_k(eachN×N) defining the Lindblad dissipators. If you have physical ratesγ_k, scale your operators asL_k ← √γ_k · C_kbefore passing them.
- Returns:
The full Liouville-space generator \(\mathcal{L}\) suitable for acting on
vec(ρ)with the column-stacking convention.
Notes:
The mapping uses
vec(O ρ) = (I ⊗ O^{\mathsf{T}}) vec(ρ)andvec(ρ O) = (O^{\mathsf{T}} ⊗ I) vec(ρ).Henters only through the coherent commutator; the dissipator depends solely onLs.All arrays are combined with NumPy
kron; performance-critical paths may prefer sparse representations.
- property nuclei
Flattened list of all nuclei across molecules.
- observable_projection_operator(state)[source]
Projection operator used to compute observable expectation values.
Simply forwards to
projection_operator()in Hilbert space.- Return type:
- property particles
Concatenated list of electron spins followed by nuclei.
- product_operator(idx1, idx2, h=1.0)[source]
Construct the (1D) product operator.
Construct the 1D (isotropic) product operator of two particles in the spin system.
- product_operator_3d(idx1, idx2, h)[source]
Construct the 3D product operator.
Construct the 3D (anisotropic) product operator of two particles in the spin system.
- product_probability(obs, rhos)[source]
Calculate the probability of the observable from the densities.
- Return type:
- static product_yield(product_probability, time, k)[source]
Calculate the product yield and the product yield sum.
- projection_operator(state, T=298, chi=1.5707963267948966)[source]
Construct the projection operator corresponding to a
state.- Parameters:
- Returns:
Projection operator corresponding to the
Statestate.- Return type:
np.ndarray
- propagate(propagator, rho)[source]
Propagate the density matrix (Hilbert space).
Propagates the density matrix using the propagator obtained using the
unitary_propagatormethod.\[\rho (t) = \mathbf{U} \rho_0 \mathbf{U}^*\]See also:
unitary_propagatorandtime_evolution.- Parameters:
propagator (np.ndarray) – Unitary operator obtained via the
unitary_propagatormethod.rho (np.ndarray) – (Initial) density matrix.
- Returns:
The new density matrix after the unitary operator was applied to it.
- Return type:
np.ndarray
- property radicals
List of electron spins (one per molecule).
- spin_operator(idx, axis)[source]
Construct the spin operator.
Construct the spin operator for the particle with index
idxin theHilbertSimulation.- Parameters:
- Returns:
Spin operator for a particle in the
HilbertSimulationsystem with indexingidxand axisaxis.- Return type:
np.ndarray
- spin_orbit_hamiltonian(soc_vectors, electron_spins=None, sparse=False)[source]
Spin–orbit interaction Hamiltonian.
H_SO = sum_i (Lx_i * S_i,x + Ly_i * S_i,y + Lz_i * S_i,z)
- Parameters:
soc_vectors – list[int], optional Which electrons (0-based) to include. Default: all.
sparse (
bool) – Return sparse CSR if True, else dense ndarray.
- Returns:
ndarray or csr_matrix
- Return type:
H_SO
- time_evolution(init_state, time, H)[source]
Evolve the system through time.
See Also: -
HilbertSimulation.unitary_propagator-HilbertSimulation.propagate-LiouvilleSimulation.unitary_propagator-LiouvilleSimulation.propagate- Parameters:
init_state (State) – Initial
Stateof the density matrix (seeprojection_operator).time (np.ndarray) – An sequence of (uniform) time points, usually created using
np.arangeornp.linspace.H (np.ndarray) – Hamiltonian operator.
- Returns:
Return a sequence of density matrices evolved through
time, starting from a given initialstateusing the HamiltonianH.- Return type:
np.ndarray
Examples
>>> molecules = [Molecule.fromdb("flavin_anion", ["N5"]), ... Molecule("Z")] >>> sim = HilbertSimulation(molecules) >>> H = sim.total_hamiltonian(B0=0, J=0, D=0) >>> time = np.arange(0, 2e-6, 5e-9) >>> time.shape (400,) >>> rhos = sim.time_evolution(State.SINGLET, time, H) >>> rhos.shape (400, 12, 12)
- total_hamiltonian(B0, J, D, theta=None, phi=None, hfc_anisotropy=False)[source]
Construct the total Hamiltonian.
The total Hamiltonian is the sum of Zeeman, Hyperfine, Exchange and Dipolar Hamiltonian.
- Parameters:
B0 (float) – See
zeeman_hamiltonian.J (float) – See
exchange_hamiltonian.D (float) – See
dipolar_hamiltonian.theta (Optional[float]) – See
zeeman_hamiltonian.phi (Optional[float]) – See
zeeman_hamiltonian.hfc_anisotropy (bool) – See
hyperfine_hamiltonian.
- Returns:
The total Hamiltonian.
- Return type:
np.ndarray
- tp_quintet_minus_one_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair quintet minus one (TP-Q-1) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_quintet_minus_two_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair quintet minus two (TP-Q-2) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_quintet_plus_one_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair quintet plus one (TP-Q+1) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_quintet_plus_two_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair quintet plus two (TP-Q+2) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_quintet_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair quintet (TP-Q) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_quintet_zero_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair quintet zero (TP-Q0) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_singlet_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair singlet (TP-S) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_triplet_minus_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair triplet minus (TP-T-) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_triplet_plus_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair triplet plus (TP-T+) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_triplet_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair triplet (TP-T) subspace.
Builds the projector from electron spin operators of radicals A and B.
- tp_triplet_zero_projop(SAx, SAy, SAz, SBx, SBy, SBz)[source]
Projection operator onto the triplet-pair triplet zero (TP-T0) subspace.
Builds the projector from electron spin operators of radicals A and B.
- static unitary_propagator(H, dt)[source]
Create unitary propagator (Hilbert space).
Create unitary propagator matrices U and U* for time evolution of the density matrix in Hilbert space (for the spin Hamiltonian
H).\[\begin{split}\mathbf{U} =& \exp( -i \hat{H} t ) \\ \mathbf{U}^* =& \exp( +i \hat{H} t )\end{split}\]See also:
propagateandtime_evolution.- Parameters:
H (np.ndarray) – Spin Hamiltonian in Hilbert space.
dt (float) – Time evolution timestep.
- Returns:
Two matrices (as tensor) in Hilbert space which are used by the
propagatemethod to perform a single step in thetime_evolutionmethod.- Return type:
np.ndarray
Examples
>>> molecules = [Molecule.fromdb("flavin_anion", ["N5"]), ... Molecule("Z")] >>> sim = HilbertSimulation(molecules) >>> H = sim.total_hamiltonian(B0=0, J=0, D=0) >>> Up, Um = sim.unitary_propagator(H, 3e-9) >>> Up.shape, Um.shape ((12, 12), (12, 12))
- zeeman_hamiltonian(B0, B_axis='z', theta=None, phi=None)[source]
Construct the Zeeman Hamiltonian (1D or 3D).
Construct the Zeeman Hamiltonian based on the external magnetic field
B0. If the anglesthetaandphiare also provided, the 3D Zeeman Hamiltonian is constructed (by invoking thezeeman_hamiltonian_3d), otherwise the 1D Zeeman Hamiltonian is constructed (by invoking thezeeman_hamiltonian_1d).- Parameters:
B0 (float) – External magnetic field intensity (milli Tesla). See
zeeman_hamiltonian_1dandzeeman_hamiltonian_3d.axis (str) – Axis of the magnetic field.
theta (Optional[float]) – rotation (polar) angle between the external magnetic field and the fixed molecule. See
zeeman_hamiltonian_3d.phi (Optional[float]) – rotation (azimuth) angle between the external magnetic field and the fixed molecule. See
zeeman_hamiltonian_3d.
- Returns:
The Zeeman Hamiltonian corresponding to the system described by the
HilbertSimulationobject and the external magnetic field intensityB0and anglesthetaandphi.- Return type:
np.ndarray
- zeeman_hamiltonian_1d(B0, axis)[source]
Construct the 1D Zeeman Hamiltonian.
Construct the 1D Zeeman Hamiltonian based on the external magnetic field
B0.- Parameters:
- Returns:
The Zeeman Hamiltonian corresponding to the system described by the
HilbertSimulationobject and the external magnetic field intensityB0.- Return type:
np.ndarray
- zeeman_hamiltonian_3d(B0, theta=0, phi=0)[source]
Construct the 3D Zeeman Hamiltonian.
Construct the 3D Zeeman Hamiltonian based on the external magnetic field
B0and anglesthetaandphi.- Parameters:
- Returns:
The Zeeman Hamiltonian corresponding to the system described by the
HilbertSimulationobject and the external magnetic field intensityB0and anglesthetaandphi.- Return type:
np.ndarray
- zero_field_splitting_hamiltonian(D, E)[source]
Build the zero-field splitting (ZFS) Hamiltonian.
Constructs the second-rank ZFS contribution for (typically triplet, S=1) spins using the conventional axial/rhombic parameters
DandE. For each particle indexidx, the operator \(D (S_z^2 - \tfrac{1}{3} \mathbf{S}^2) + E (S_x^2 - S_y^2)\) is formed and summed. The parameters are scaled into frequency units via the first radical’s electron gyromagnetic ratio.- Parameters:
D – Axial ZFS parameter (in the same field/frequency units used elsewhere; internally scaled by
-radicals[0].gamma_mT).E – Rhombic ZFS parameter (scaled identically to
D).
- Return type:
- Returns:
The ZFS Hamiltonian matrix in the current simulation basis.
Notes
This implementation multiplies
DandEby-gamma_mTof the first radical to convert to angular frequency units consistent with the rest of the Hamiltonian.The loop applies the operator to every particle index; in practice, ZFS is only meaningful for S≥1 spins (e.g., an excited triplet). Ensure your particle list reflects that physical situation.
- class radicalpy.simulation.LiouvilleIncoherentProcessBase(rate_constant)[source]
Bases:
HilbertIncoherentProcessBase
- class radicalpy.simulation.LiouvilleSimulation(molecules, custom_gfactors=False, basis=Basis.ST)[source]
Bases:
HilbertSimulation- property hamiltonian_size
Dimension of the Liouville space (Hilbert dimension squared).
- initial_density_matrix(state, H)[source]
Create an initial density matrix for time evolution of the spin Hamiltonian density matrix.
- liouville_projection_operator(state)[source]
Column-vectorised projector for Liouville space.
Flattens the Hilbert-space projector associated with
stateto shape(N², 1)for use with Liouville evolution.- Return type:
- observable_projection_operator(state)[source]
Row-vectorised observable (projectorᵀ) used for expectation values.
- Return type:
- propagate(propagator, rho)[source]
Propagate the density matrix (Liouville space).
Propagates the density matrix using the propagator obtained using the
unitary_propagatormethod.\[\rho (t) = \mathbf{U} \rho_0\]See also:
unitary_propagatorandtime_evolution.- Parameters:
propagator (np.ndarray) – Unitary operator obtained via the
unitary_propagatormethod.rho (np.ndarray) – (Initial) density matrix.
- Returns:
The new density matrix after the unitary operator was applied to it.
- Return type:
np.ndarray
- static unitary_propagator(H, dt)[source]
Create unitary propagator (Liouville space).
Create unitary propagator matrix U for the time evolution of the density matrix in Liouville space (for the spin Hamiltonian
H).\[\mathbf{U} = \exp( \hat{\hat{L}} t )\]See also:
propagateandtime_evolution.- Parameters:
H (np.ndarray) – Spin Hamiltonian in Liouville space.
dt (float) – Time evolution timestep.
- Returns:
The matrix in Liouville space which is used by the
propagatemethod to perform a single step in thetime_evolutionmethod.- Return type:
np.ndarray
Examples
>>> molecules = [Molecule.fromdb("flavin_anion", ["N5"]), ... Molecule("Z")] >>> sim = LiouvilleSimulation(molecules) >>> H = sim.total_hamiltonian(B0=0, J=0, D=0) >>> sim.unitary_propagator(H, 3e-9).shape (144, 144)
- class radicalpy.simulation.SemiclassicalSimulation(molecules, custom_gfactors=False, basis=Basis.ST)[source]
Bases:
LiouvilleSimulation- external_field_mT()[source]
Lab field B0 in mT as a 3-vector. Override if you have a field in the simulation.
- Return type:
- property nuclei
Semiclassical model replaces nuclei by random static fields; return empty list.
- semiclassical_HHs(num_samples, *, anisotropic=False)[source]
Generate semiclassical Hamiltonians.
- For each radical r:
draw a static random hyperfine field B_hf,r (3D Gaussian), add the external field B0, then convert to ω_r = γ_r * (B0 + B_hf,r), and construct H = Σ_r ω_r · S_r. Repeat num_samples times.
- class radicalpy.simulation.SparseCholeskyHilbertSimulation(molecules, custom_gfactors=False, basis=Basis.ST)[source]
Bases:
HilbertSimulationA simulation class that exploits
Sparsity of the Hamiltonian
Cholesky decomposition of the density matrix
Since density matrices are positive semi-definite, they can be decomposed into
rho = X X^T
where X is a lower triangular matrix by Cholesky decomposition.
In particular, when rho is a diagonal matrix, X = sqrt(rho).
This class accelerates the time evolution for the system with large Hilbert space (> 10^3).
- ST_basis(M)[source]
Sparse S/T-basis transform of an operator.
Accepts dense or sparse input, converts to sparse as needed, and applies the electron-only change-of-basis while preserving sparsity.
- Return type:
sparray
- get_eye(shape)[source]
Return a sparse identity matrix of the requested dimension.
- Return type:
sparray
- product_probability(obs, rhos)[source]
Calculate the probability of the observable from the densities.
- Return type:
- propagate(propagator, rho)[source]
Propagate a Cholesky factorisation
(X, Xᵀ)one step.Uses
Um @ Xto advance the factor; the updated pair(UmX, (UmX)ᴴ)represents the new density without formingρ = X Xᵀexplicitly. Automatically switches to dense math when sparsity falls below a threshold (~30% zeros in factors).
- spin_operator(idx, axis)[source]
Construct the spin operator.
Construct the spin operator for the particle with index
idxin theHilbertSimulation.- Parameters:
- Returns:
Spin operator for a particle in the
HilbertSimulationsystem with indexingidxand axisaxis.- Return type:
np.ndarray
- time_evolution(init_state, time, H)[source]
Evolve the system through time.
See Also: -
HilbertSimulation.unitary_propagator-HilbertSimulation.propagate- Parameters:
- Returns:
Return a sequence of density matrices (X, X^T) evolved through
time, starting from a given initialstateusing the HamiltonianH. Density matrices are obtained by X X^T.- Return type:
tuple[np.ndarray, np.ndarray]
Examples
>>> molecules = [Molecule.fromdb("flavin_anion", ["N5"]), ... Molecule("Z")] >>> sim = SparseCholeskyHilbertSimulation(molecules) >>> H = sim.total_hamiltonian(B0=0, J=0, D=0) >>> time = np.arange(0, 2e-6, 5e-9) >>> time.shape (400,) >>> rhos = sim.time_evolution(State.SINGLET, time, H) >>> len(rhos) 400 >>> rhos[0][0].shape (12, 12) >>> rhos[0][1].shape (12, 12)
- class radicalpy.simulation.State(value)[source]
Bases:
EnumEnumeration of common spin/population states used as observables or initial conditions.
- CISS = 'CISS'
- EPR = 'EPR'
- EQUILIBRIUM = 'Eq'
- SECOND_ORDER_POLARISATION = '2ndOrderPol'
- SINGLET = 'S'
- TP_QUINTET = 'TP_Q'
- TP_QUINTET_MINUS_ONE = 'TP_Q-1'
- TP_QUINTET_MINUS_TWO = 'TP_Q-2'
- TP_QUINTET_PLUS_ONE = 'TP_Q+1'
- TP_QUINTET_PLUS_TWO = 'TP_Q+2'
- TP_QUINTET_ZERO = 'TP_Q0'
- TP_SINGLET = 'TP_S'
- TP_TRIPLET = 'TP_T'
- TP_TRIPLET_MINUS = 'TP_T-'
- TP_TRIPLET_PLUS = 'TP_T+'
- TP_TRIPLET_ZERO = 'TP_T0'
- TRIPLET = 'T'
- TRIPLET_MINUS = 'T_-'
- TRIPLET_PLUS = 'T_+'
- TRIPLET_PLUS_MINUS = 'T_\\pm'
- TRIPLET_X = 'X'
- TRIPLET_Y = 'Y'
- TRIPLET_Z = 'Z'
- TRIPLET_ZERO = 'T_0'
radicalpy.tensornetwork module
Tensor network simulations for radical pair dynamics.
This module provides comprehensive tensor network-based simulations for radical pair dynamics, implementing three different approaches:
Stochastic Matrix Product State (SMPS): Monte Carlo sampling approach in Hilbert space
Locally Purified Matrix Product State (LPMPS): Purification-based deterministic approach in Hilbert space
Vectorised Matrix Product Density Operator (VMPDO): Deterministic approach in Liouville space
The simulations support quantum mechanical treatments of nuclear spins, allowing for accurate modeling of: - Hyperfine interactions (isotropic and anisotropic) - Exchange coupling between radicals - Dipolar interactions - Zeeman effects - Haberkorn recombination terms
- Key Features:
Support for both Hermitian and non-Hermitian Hamiltonians
Multiple computational backends (NumPy, JAX)
Parallel processing capabilities
GPU acceleration support (via JAX)
Example
Basic usage examples are available in:
examples/lpmps.py- Linear product MPS simulationsexamples/smps.py- Stochastic MPS simulationsexamples/vmpdo.py- Vectorised MPDO simulations
from radicalpy.data import Molecule
from radicalpy.simulation import State
import radicalpy.tensornetwork as tn
# Create radical pair molecules
molecules = [Molecule(...), Molecule(...)]
# Define system parameters
J, D, B0, kS, kT, anisotropy = ...
# Initialize simulation
sim = tn.LocallyPurifiedMPSSimulation(
molecules=molecules,
bond_dimension=16,
jobname="lpmps",
)
# Construct tensor network Hamiltonian
H = sim.total_hamiltonian(
B0=B0,
J=J,
D=D,
kS=kS,
kT=kT,
hfc_anisotropy=anisotropy,
)
# Run simulation
partial_trace = sim.time_evolution(
init_state=State.SINGLET,
time=np.linspace(0, 1e-6, 1000),
H=H
)
# Get population of singlet state
singlet_population = sim.product_probability(
obs=State.SINGLET,
rhos=partial_trace,
)
- Dependencies:
Install the required tensor network simulation libraries:
pip install git+https://github.com/QCLovers/PyTDSCF
References
- class radicalpy.tensornetwork.BaseMPSSimulation(molecules, custom_gfactors=False, basis=Basis.ST, bond_dimension=16, backend='numpy', integrator='arnoldi', jobname='tensornetwork')[source]
Bases:
HilbertSimulation,ABCAbstract base class for matrix product state simulations.
This class provides the foundation for tensor network-based simulations of radical pair dynamics. It implements common functionality for constructing Hamiltonians and managing tensor network operations.
The class supports three concrete implementations: -
StochasticMPSSimulation: Monte Carlo sampling approach -LocallyPurifiedMPSSimulation: Purification-based method -MPDOSimulation: Direct density operator approach- Key Features:
Support for various Hamiltonian terms (Zeeman, hyperfine, exchange, dipolar)
Automatic matrix product operator (MPO) construction
Multiple computational backends (NumPy, JAX)
- Parameters:
molecules (list[Molecule]) – List of molecules in the radical pair.
custom_gfactors (bool) – Whether to use custom g-factors. Default is False.
basis (Basis) – Basis set for the simulation. Only ST basis is supported.
bond_dimension (int) – Bond dimension for the matrix product state. Default is 16.
backend (Literal["numpy", "jax", "auto"]) – Computational backend. ‘auto’ selects ‘jax’ for bond_dimension > 32, ‘numpy’ otherwise.
integrator (Literal["arnoldi", "lanczos"]) – Integration method for time evolution. Use ‘lanczos’ for Hermitian Hamiltonians, ‘arnoldi’ for non-Hermitian.
jobname (str) – Job name for output files. Default is “tensornetwork”.
- Raises:
ModuleNotFoundError – If required dependencies (PyTDSCF, PyMPO) are not installed.
NotImplementedError – If basis other than ST is specified.
Note
Important differences from standard HilbertSimulation:
Nuclear spins are automatically sorted by hyperfine coupling strength to reduce entanglement in the tensor network representation
Only ST basis is supported (Zeeman basis not available)
Each term of the Hamiltonian returns SumOfProducts objects instead of numpy arrays
The total Hamiltonian returns a MPO (list of numpy arrays) instead of a single numpy array
Time evolution returns diagonal elements of reduced density matrix (shape: nsteps, 4) or full reduced density matrices (shape: nsteps, 4, 4) instead of full density matrices (shape: nsteps, dim, dim)
Requires external dependencies (PyTDSCF, PyMPO) for tensor operations
- ST_basis(M)[source]
Transform an operator from the Zeeman basis to the S/T basis.
- Parameters:
M – Operator in the Zeeman basis.
- Returns:
The operator expressed in the S/T basis.
- apply_liouville_hamiltonian_modifiers(H, modifiers)[source]
apply_liouville_hamiltonian_modifiers is not used for BaseMPSSimulation. For Haberkorn term, include kS and kT for
total_hamiltonian().
- dipolar_hamiltonian(D)[source]
Construct the dipolar Hamiltonian as a SumOfProducts.
- Parameters:
D (float | np.ndarray) – Dipolar coupling constant or tensor in mT.
- Returns:
The dipolar Hamiltonian as a sum of products of operators.
- Return type:
SumOfProducts
- exchange_hamiltonian(J, prod_coeff=2)[source]
Construct the exchange Hamiltonian as a SumOfProducts.
- haberkorn_hamiltonian(kS, kT)[source]
Construct the Haberkorn recombination term as a SumOfProducts.
- hyperfine_hamiltonian(hfc_anisotropy=False)[source]
Construct the hyperfine Hamiltonian as a SumOfProducts.
- Parameters:
hfc_anisotropy (bool) – Whether to include anisotropic hyperfine coupling. Default is False.
- Returns:
The hyperfine Hamiltonian as a sum of products of operators.
- Return type:
SumOfProducts
- initial_density_matrix(state, H)[source]
Initial density matrix is not used for BaseMPSSimulation.
- Return type:
- observable_projection_operator(state)[source]
Projection operator used to compute observable expectation values.
Simply forwards to
projection_operator()in Hilbert space.- Return type:
- product_probability(obs, rhos)[source]
Calculate the probability of the observable from the densities.
- Return type:
- propagate(propagator, rho)[source]
Propagate is not used for BaseMPSSimulation. Use
time_evolution()instead.- Return type:
- spin_operator(idx, axis)[source]
Construct the spin operator.
Construct the spin operator for the particle with index
idxin theHilbertSimulation.
- abstract time_evolution(init_state, time, H)[source]
Evolve the system through time.
See Also: -
HilbertSimulation.unitary_propagator-HilbertSimulation.propagate-LiouvilleSimulation.unitary_propagator-LiouvilleSimulation.propagate- Parameters:
init_state (State) – Initial
Stateof the density matrix (seeprojection_operator).time (np.ndarray) – An sequence of (uniform) time points, usually created using
np.arangeornp.linspace.H (np.ndarray) – Hamiltonian operator.
- Returns:
Return a sequence of density matrices evolved through
time, starting from a given initialstateusing the HamiltonianH.- Return type:
np.ndarray
Examples
>>> molecules = [Molecule.fromdb("flavin_anion", ["N5"]), ... Molecule("Z")] >>> sim = HilbertSimulation(molecules) >>> H = sim.total_hamiltonian(B0=0, J=0, D=0) >>> time = np.arange(0, 2e-6, 5e-9) >>> time.shape (400,) >>> rhos = sim.time_evolution(State.SINGLET, time, H) >>> rhos.shape (400, 12, 12)
- total_hamiltonian(B0, J, D, theta=None, phi=None, hfc_anisotropy=False, kS=0.0, kT=0.0)[source]
Construct the total Hamiltonian as a matrix product operator.
- Parameters:
B0 (float) – Magnetic field strength in mT.
J (float) – Exchange coupling constant in mT.
D (float | np.ndarray) – Dipolar coupling constant or tensor in mT.
theta (float, optional) – Polar angle of the magnetic field in radians.
phi (float, optional) – Azimuthal angle of the magnetic field in radians.
hfc_anisotropy (bool) – Whether to include anisotropic hyperfine coupling. Default is False.
kS (float) – Singlet recombination rate in s^-1. Default is 0.0.
kT (float) – Triplet recombination rate in s^-1. Default is 0.0.
- Returns:
Total Hamiltonian as a matrix product operator.
- Return type:
list[np.ndarray]
- static unitary_propagator(H, dt)[source]
Unitary propagator is not used for BaseMPSSimulation. Use
time_evolution()instead.
- class radicalpy.tensornetwork.LocallyPurifiedMPSSimulation(molecules, custom_gfactors=False, basis=Basis.ST, bond_dimension=16, integrator='arnoldi', backend='auto', jobname='lpmps')[source]
Bases:
BaseMPSSimulationLocally purified matrix product state simulation for radical pair dynamics.
This class implements locally purified matrix product state (LPMPS) simulations for mixed quantum states. LPMPS uses purification to represent mixed states as pure states in an enlarged Hilbert space, enabling efficient simulation of decoherence and relaxation processes.
The purification approach works by: 1. Doubling the physical Hilbert space with auxiliary degrees of freedom 2. Representing mixed states as pure states in the enlarged space 3. Propagating the purified state using tensor network methods 4. Tracing out auxiliary degrees of freedom to obtain physical observables
- Parameters:
molecules (list[Molecule]) – List of molecules in the radical pair.
custom_gfactors (bool) – Whether to use custom g-factors. Default is False.
basis (Basis) – Basis set for the simulation. Only ST basis is supported.
bond_dimension (int) – Bond dimension for the matrix product state. Default is 16.
integrator (Literal["arnoldi", "lanczos"]) – Integration method for time evolution. Use ‘lanczos’ for Hermitian Hamiltonians, ‘arnoldi’ for non-Hermitian.
backend (Literal["numpy", "jax", "auto"]) – Computational backend. ‘auto’ selects ‘jax’ for bond_dimension > 32, ‘numpy’ otherwise.
jobname (str) – Job name for output files. Default is “lpmps”.
Note
Important differences from standard simulations:
Uses purification approach with auxiliary degrees of freedom (approximately twice the computational resources compared to pure state simulations)
Only State.SINGLET initial state is supported (other states raise NotImplementedError)
Returns diagonal elements of reduced density matrix instead of full density matrices
Requires proper site ordering for auxiliary degrees of freedom
Uses Hilbert space formulation with doubled physical space
Example
from radicalpy.tensornetwork import LocallyPurifiedMPSSimulation sim = LocallyPurifiedMPSSimulation( molecules=molecules, bond_dimension=64, backend="jax" # For GPU acceleration ) result = sim.time_evolution(init_state=State.SINGLET, time=time, H=H)
- time_evolution(init_state, time, H)[source]
Evolve the system through time.
- Parameters:
- Returns:
Diagonal elements of the reduced density matrix.
- Return type:
np.ndarray
- total_hamiltonian(B0, J, D, theta=None, phi=None, hfc_anisotropy=False, kS=0.0, kT=0.0)[source]
Construct the total Hamiltonian as a matrix product operator.
- Parameters:
B0 (float) – Magnetic field strength in mT.
J (float) – Exchange coupling constant in mT.
D (float | np.ndarray) – Dipolar coupling constant or tensor in mT.
theta (float, optional) – Polar angle of the magnetic field in radians.
phi (float, optional) – Azimuthal angle of the magnetic field in radians.
hfc_anisotropy (bool) – Whether to include anisotropic hyperfine coupling. Default is False.
kS (float) – Singlet recombination rate in s^-1. Default is 0.0.
kT (float) – Triplet recombination rate in s^-1. Default is 0.0.
- Returns:
Total Hamiltonian as a matrix product operator.
- Return type:
list[np.ndarray]
- class radicalpy.tensornetwork.MPDOSimulation(molecules, custom_gfactors=False, basis=Basis.ST, bond_dimension=16, integrator='arnoldi', backend='auto', jobname='vmpdo')[source]
Bases:
BaseMPSSimulationVectorised matrix product density operator simulation for radical pair dynamics.
This class implements vectorised matrix product density operator (VMPDO) simulations for mixed quantum states. VMPDO directly represents density operators as matrix product operators, enabling efficient simulation of decoherence, relaxation, and other open quantum system dynamics.
The VMPDO approach works by: 1. Reshape matrix product density operator into matrix product state 2. Propagating the density operator using Liouville space dynamics 3. Computing observables as traces of operator products 4. Supporting both Hermitian and non-Hermitian evolution
- Parameters:
molecules (list[Molecule]) – List of molecules in the radical pair.
custom_gfactors (bool) – Whether to use custom g-factors. Default is False.
basis (Basis) – Basis set for the simulation. Only ST basis is supported.
bond_dimension (int) – Bond dimension for the matrix product operator. Default is 16.
integrator (Literal["arnoldi", "lanczos"]) – Integration method for time evolution. Use ‘lanczos’ for Hermitian Hamiltonians, ‘arnoldi’ for non-Hermitian.
backend (Literal["numpy", "jax", "auto"]) – Computational backend. ‘auto’ selects ‘jax’ for bond_dimension > 32, ‘numpy’ otherwise.
jobname (str) – Job name for output files. Default is “vmpdo”.
Note
Returns reduced density matrix instead of full density matrice
Trace preserving, positivity preserving, and completely positive evolution is not guaranteed
Example
from radicalpy.tensornetwork import MPDOSimulation sim = MPDOSimulation( molecules=molecules, bond_dimension=64, backend="jax" # For GPU acceleration ) result = sim.time_evolution(init_state=State.SINGLET, time=time, H=H)
- dipolar_hamiltonian(D)[source]
Construct the dipolar Hamiltonian as a SumOfProducts.
- Parameters:
D (float | np.ndarray) – Dipolar coupling constant or tensor in mT.
- Returns:
The dipolar Hamiltonian as a sum of products of operators.
- Return type:
SumOfProducts
- exchange_hamiltonian(J, prod_coeff=2)[source]
Construct the exchange Hamiltonian as a SumOfProducts.
- haberkorn_hamiltonian(kS, kT)[source]
Construct the Haberkorn recombination term as a SumOfProducts.
- hyperfine_hamiltonian(hfc_anisotropy=False)[source]
Construct the hyperfine Hamiltonian as a SumOfProducts.
- Parameters:
hfc_anisotropy (bool) – Whether to include anisotropic hyperfine coupling. Default is False.
- Returns:
The hyperfine Hamiltonian as a sum of products of operators.
- Return type:
SumOfProducts
- time_evolution(init_state, time, H)[source]
Evolve the system through time.
- Parameters:
- Returns:
Reduced density matrix with shape (nsteps, 4, 4).
- Return type:
np.ndarray
- class radicalpy.tensornetwork.StochasticMPSSimulation(molecules, custom_gfactors=False, basis=Basis.ST, bond_dimension=16, integrator='arnoldi', nsamples=128, max_workers=8, jobname='smps')[source]
Bases:
BaseMPSSimulationStochastic matrix product state simulation for radical pair dynamics.
This class implements stochastic matrix product state (SMPS) simulations using Monte Carlo sampling to generate trajectories of radical pair states. SMPS is particularly effective for high-dimensional quantum systems where exact simulation becomes computationally prohibitive.
The simulation works by: 1. Sampling from spin coherent states for nuclear spins 2. Propagating each sample using tensor network methods 3. Averaging over all samples to obtain ensemble properties
- Parameters:
molecules (list[Molecule]) – List of molecules in the radical pair.
custom_gfactors (bool) – Whether to use custom g-factors. Default is False.
basis (Basis) – Basis set for the simulation. Only ST basis is supported.
bond_dimension (int) – Bond dimension for the matrix product state. Default is 16.
integrator (Literal["arnoldi", "lanczos"]) – Integration method for time evolution. Use ‘lanczos’ for Hermitian Hamiltonians, ‘arnoldi’ for non-Hermitian.
nsamples (int) – Number of Monte Carlo samples to generate. Default is 128.
max_workers (int) – Maximum number of parallel workers for sampling. Default is 8.
jobname (str) – Job name for output files. Default is “smps”.
- Raises:
RuntimeError – If thread number environment variables are not set.
Note
Important differences from standard simulations:
Only State.SINGLET initial state is supported (other states raise NotImplementedError)
Returns diagonal elements of reduced density matrix instead of full density matrices
Uses Monte Carlo sampling with statistical averaging over nsamples trajectories
Requires proper thread configuration. Set one of the following environment variables before importing: OMP_NUM_THREADS, OPENBLAS_NUM_THREADS, MKL_NUM_THREADS, VECLIB_MAXIMUM_THREADS, or NUMEXPR_NUM_THREADS
Uses multiprocessing with fork method for parallel execution
Example
import os os.environ["OMP_NUM_THREADS"] = "1" from radicalpy.tensornetwork import StochasticMPSSimulation sim = StochasticMPSSimulation( molecules=molecules, bond_dimension=32, nsamples=256, max_workers=4 ) diagonal_elements = sim.time_evolution(init_state=State.SINGLET, time=time, H=H)
- time_evolution(init_state, time, H)[source]
Evolve the system through time using stochastic matrix product states.
This method performs time evolution using Monte Carlo sampling of spin coherent states. Each sample is propagated independently using tensor network methods, and the results are averaged to obtain ensemble properties.
The simulation process: 1. Generates random spin coherent states for nuclear spins 2. Propagates each sample using the provided Hamiltonian 3. Computes reduced density matrix for each sample 4. Averages over all samples to obtain ensemble properties
- Parameters:
init_state (State) – Initial state of the system. Only State.SINGLET is currently supported for stochastic simulations.
time (np.ndarray) – Array of time points for the evolution, typically created using np.linspace or np.arange. Must be uniformly spaced.
H (list[np.ndarray]) – Hamiltonian as a matrix product operator. Each element represents a tensor in the MPO.
- Returns:
- Diagonal elements of the reduced density matrix with
shape (nsteps, 4) representing [T+, T0, S, T-] populations.
- Return type:
np.ndarray
- Raises:
NotImplementedError – If init_state is not State.SINGLET.
Note
Critical differences from standard time evolution:
Returns diagonal elements of reduced density matrix (shape: nsteps, 4) instead of full density matrices (shape: nsteps, dim, dim)
Only State.SINGLET initial state is supported
Uses stochastic sampling over nuclear spin coherent states
Requires statistical averaging over multiple Monte Carlo samples
Uses parallel processing with multiprocessing for efficiency
Example
import numpy as np from radicalpy import State # Create time array time = np.linspace(0, 1e-6, 1000) # Run simulation result = sim.time_evolution( init_state=State.SINGLET, time=time, H=hamiltonian ) # result.shape is (1000, 4) # result[:, 2] contains singlet population
radicalpy.utils module
Utilities for spin dynamics, magnetic‐resonance and molecular simulations.
This module collects small, reusable helpers used across the codebase: unit conversions, geometry on spheres, spectrum building blocks, I/O parsers for molecular files and quantum-chemistry outputs, and a few domain-specific utilities for MARY/NMR workflows.
Main contents:
- Constants
COVALENT_RADII: Covalent radii (Å) for common elements; used bybonding heuristics and label/element inference.
- CLI/testing
is_fast_run(): Check--fastCLI flag to run lighter examples/tests.- Fitting & line shapes
Bhalf_fit(B, MARY): Fit MARY data to a Lorentzian to obtain\(B_{1/2}\) and goodness-of-fit. -
Lorentzian(B, amplitude, Bhalf): MARY Lorentzian model.- Unit conversions
Gauss_to_mT/MHz/…,MHz_to_mT/Gauss/…,
mT_to_MHz/Gauss/…,angular_frequency_in_MHz,*_to_angular_frequency: Consistent conversions between G, mT, MHz and rad·s⁻¹·T⁻¹. Consistent conversions between GHz, J, meV, mK.- Angular grids & spherical geometry
anisotropy_check(theta, phi): Validate angle grids and parity (θ odd/φ even).
_check_full_sphere(theta, phi): Ensure full-sphere linspace grids.
spherical_average(...): Weighted average over a θ/φ grid.
cartesian_to_spherical(...),spherical_to_cartesian(...):Coordinate transforms.
- Autocorrelation
autocorrelation(data, factor=1): FFT-based trajectory autocorrelation.- CIDNP helper models
cidnp_polarisation_*: Exponential, truncated diffusion, and fulldiffusion models; see docstrings for details. -
s_t0_omega(deltag, B0, hfc_star, onuc_all): ω₊/ω₋ for S–T₀ mixing.- NMR building blocks
nmr_chemical_shift_real/im...: cos/sin(2π f t) modulators.
nmr_scalar_coupling_modulation: J-modulation term.
nmr_t2_relaxation: exp(−t/T₂) envelope.- Lock-in MARY helpers
modulated_signal(...),reference_signal(...):Time-domain modulation and reference waves. -
mary_lorentzian(mod_signal, lfe_magnitude): Simple MARY line shape.- Molecular geometry & visualization
define_xyz(...): Construct an orthonormal (x,y,z) basis fromatom/point triplets. -
get_angle_between_plane(A, B): Angle (deg) between plane normals. -get_rotation_matrix_euler_angles(A, B): Rotation matrix and ZXZ Euler angles mapping basis A→B. -rodrigues_rotation(v, k, theta): Rotate vectors around axis k by θ. -rotate_axes(A, x, y, z): Apply a rotation matrix to a basis. -enumerate_spin_states_from_base(base): Mixed-radix enumeration of spin projections (e.g., 2 for spin-½, 3 for spin-1). -infer_bonds(elements, coords, ...): Heuristic covalent bonding.- File I/O & parsing
parse_xyz(path),parse_label_xyz_txt(path),parse_pdb(path, ...):Read coordinates and optional bonds/labels for plotting. -
pdb_label(atom, scheme=...): Human-readable PDB atom labels. -write_xyz/mol_to_plot_arrays/write_pdb/write_sdf: Minimal writers and plotting helpers. -read_trajectory_files(dir, scale): Concatenate text trajectories from a folder.- Quantum-chemistry integration (ORCA)
read_orca_hyperfine(path, version=6): Dispatch to ORCA v6 parsers.Internal:
_hyperfine_from_orca6_out/_hyperfine_from_orca6_property_txt.Return zero-based nucleus indices, isotope labels, and 3×3 HFC tensors (mT). -
read_lines_utf8_or_utf16le: Robust text decoding for ORCA outputs. -write_orca_from_pdb: Creates customisable ORCA input files.- Quantum quantification methods
negativity: Computes the (logarithmic) negativity of a multipartite density matrix.A measure of entanglement.
purity: Calculates the purity of a density matrix.
von_neumann_entropy: Computes the von Neumann entropy of a quantum state.- Chemistry utilities (RDKit)
smiles_to_3d(smiles, add_h=True, opt='mmff'): Generate a 3D conformer(ETKDG) with optional MMFF/UFF optimisation. -
mol_to_plot_arrays(mol): Extract elements, labels, coords and bonds.- Spectral density
spectral_density(omega, tau_c): \(J(ω) = τ_c / (1 + ω^2 τ_c^2)\).
Notes
Many routines are vectorised and expect NumPy arrays; see individual
docstrings for shapes and units.
- Angle conventions use radians: theta ∈ [0, π], phi ∈ [0, 2π].
- Unit conversions rely on physical constants from radicalpy.constants (C).
- radicalpy.utils.Bhalf_LFEhalf_fit(B, MARY, LFE_position=1.0)[source]
B_1/2 and LFE_1/2 fitting for MARY spectra.
- Parameters:
B (np.ndarray) – Magnetic field values (x-axis).
MARY (np.ndarray) – Magnetic field effect data (y-axis). Use the
MARYentry in the result ofradicalpy.simulation.HilbertSimulation.MARY.LFE_position (float) – Initial guess for the low field effect position.
- Returns:
Bhalf(float): The magnetic field strength at half the saturation magnetic field.LFEhalf(float): The magnetic field strength at half the low field effect magnetic field.fit_result(np.ndarray): y-axis from fit.fit_error(float): Standard error for the fit.R2(float): R-squared value for the fit.
- Return type:
- radicalpy.utils.Bhalf_fit(B, MARY)[source]
B_1/2 fit for MARY spectra.
- Parameters:
B (np.ndarray) – Magnetic field values (x-axis).
MARY (np.ndarray) – Magnetic field effect data (y-axis). Use the
MARYentry in the result ofradicalpy.simulation.HilbertSimulation.MARY.
- Returns:
Bhalf(float): The magnetic field strength at half the saturation magnetic field.fit_result(np.ndarray): y-axis from fit.fit_error(float): Standard error for the fit.R2(float): R-squared value for the fit.
- Return type:
- radicalpy.utils.GHz_to_mK(GHz)[source]
Convert a frequency from GHz to mK using h ν = k_B T.
- Relation:
h · (1e9 · ν_GHz) = k_B · (1e-3 · T_mK) ⇒ T_mK = 1e12 · (h / k_B) · ν_GHz
- radicalpy.utils.GHz_to_meV(GHz)[source]
Convert an energy/frequency from GHz to meV.
Uses 1 GHz = 4.1357×10⁻³ meV.
- radicalpy.utils.J_to_meV(J)[source]
Convert an energy from Joule (J) to meV.
Uses 1 eV = 1.602176565×10⁻¹⁹ J.
- radicalpy.utils.Lorentzian(B, amplitude, Bhalf)[source]
Lorentzian function for MARY spectra.
More information in
radicalpy.utils.Bhalf_fit(where this is used).
- radicalpy.utils.anisotropy_check(theta, phi)[source]
Validate and normalise anisotropy angle grids.
Accepts scalar or array inputs for the polar (
theta) and azimuthal (phi) angles, promotes scalars to 1-element arrays, and enforces domain and parity constraints that downstream integrators rely on.- Parameters:
theta (float or np.ndarray) – Polar angle(s) in radians. Must lie in
[0, π]. If an array is provided andlen(theta) > 1whilelen(phi) > 1, thenlen(theta)must be odd.phi (float or np.ndarray) – Azimuthal angle(s) in radians. Must lie in
[0, 2π]. If an array is provided andlen(theta) > 1whilelen(phi) > 1, thenlen(phi)must be even.
- Returns:
The validated angle arrays
(theta, phi)(possibly promoted from scalars).- Return type:
(np.ndarray, np.ndarray)
- Raises:
ValueError – If angles are out of range or the grid parity constraints are violated.
- radicalpy.utils.autocorrelation(data, factor=1)[source]
Calculate the autocorrelation of a trajectory.
An FFT-based implementation of the autocorrelation for Monte Carlo or molecular dynamics trajectories (or any other time dependent value).
- Parameters:
data (np.ndarray) – The time dependent trajectory.
factor (int) – Data length reduction factor.
- Returns:
The autocorrelation of the trajectory.
- Return type:
np.ndarray
- radicalpy.utils.cartesian_to_spherical(x, y, z)[source]
Convert Cartesian coordinates to spherical coordinates.
- Parameters:
- Returns:
The radial distance(s). theta (float or np.ndarray): The polar angle(s). phi (float or np.ndarray): The azimuthal angle(s).
- Return type:
r (float or np.ndarray)
- radicalpy.utils.cidnp_polarisation_diffusion_model(omega_plus, omega_minus, alpha=1.5)[source]
Compute the CIDNP polarisation using the full diffusion model.
- radicalpy.utils.cidnp_polarisation_exponential_model(ks, omega_plus, omega_minus)[source]
Compute the CIDNP polarisation using the exponential model.
- radicalpy.utils.cidnp_polarisation_truncated_diffusion_model(omega_plus, omega_minus)[source]
Compute the CIDNP polarisation using the truncated t^{-3/2} diffusion model.
- Parameters:
omega_plus (np.ndarray) – Array of omega+ frequencies (rad/s).
omega_minus (np.ndarray) – Array of omega- frequencies (rad/s).
- Returns:
CIDNP polarisation.
- Return type:
p (float)
- radicalpy.utils.define_xyz(x1, x2, z1, z2, z3, z4)[source]
Construct a right-handed orthonormal basis from atom/point triplets.
The z-axis is defined as the normal to the plane spanned by the vectors
(z1 − z2)and(z3 − z4)(right-hand rule). The x-axis is the normalised direction fromx2tox1. The y-axis completes the right-handed triad viay = z × x, and x is re-orthogonalised byx = y × z.- Parameters:
x1 (array-like) – Points defining the x-axis direction.
x2 (array-like) – Points defining the x-axis direction.
z1 (array-like) – Points defining two in-plane vectors whose cross product yields the z-axis.
z2 (array-like) – Points defining two in-plane vectors whose cross product yields the z-axis.
z3 (array-like) – Points defining two in-plane vectors whose cross product yields the z-axis.
z4 (array-like) – Points defining two in-plane vectors whose cross product yields the z-axis.
- Returns:
Unit vectors
(x, y, z)forming a right-handed orthonormal basis.- Return type:
(np.ndarray, np.ndarray, np.ndarray)
- radicalpy.utils.double_Lorentzian(B, amplitude, Bhalf, LFE_amplitude, LFEhalf)[source]
Double Lorentzian function for MARY spectra.
More information in
radicalpy.utils.Bhalf_fit(where this is used).- Parameters:
B (np.ndarray) – The x-axis magnetic field values.
amplitude (float) – The amplitude of the saturation field value.
Bhalf (float) – The magnetic field strength at half the saturation field value.
LFE_amplitude (float) – The amplitude of the LFE component.
LFEhalf (float) – The magnetic field strength at half the LFE field value.
- Returns:
Double Lorentzian function for MARY spectrum.
- Return type:
np.ndarray
- radicalpy.utils.eigensorter(H)[source]
Diagonalise a square matrix and return eigenvalues in ascending order together with the corresponding (row-stacked) eigenvectors.
This helper wraps
numpy.linalg.eig(), sorts the eigenpairs by ascending eigenvalue, and returns the eigenvectors transposed so that each eigenvector appears as a row in the returned array.:param For Hermitian inputs consider using
numpy.linalg.eigh(): :param elsewhere for improved numerical stability: :param but this routine: :param intentionally useseigto support non-Hermitian cases.:- Returns:
Eigenvalues sorted in ascending order.
evecs (ndarray of shape (N, N)): Row-stacked eigenvectors corresponding to
evals. Each rowkis the eigenvectorv_ksatisfying (approximately)H @ v_k = evals[k] * v_k.- Return type:
evals (ndarray of shape (N,))
- radicalpy.utils.enumerate_spin_states_from_base(base)[source]
Return all spin-state patterns for a mixed-radix ‘base’ (e.g. [2,2,3,…]). Each row corresponds to one configuration. For base[i]=b, the digit d in [0..b-1] maps to spin projection m = (b-1)/2 - d.
- Return type:
- radicalpy.utils.get_angle_between_plane(A, B)[source]
Angle between two plane normals in degrees.
Computes the angle between vectors
AandB(interpreted as plane normals). The result is within[0, 180]degrees.- Parameters:
A (array-like) – First normal vector.
B (array-like) – Second normal vector.
- Returns:
Angle between
AandBin degrees.- Return type:
- radicalpy.utils.get_rotation_matrix_euler_angles(A, B)[source]
Rotation matrix and ZXZ Euler angles mapping basis A → basis B.
Forms the rotation matrix
R = Aᵀ Bthat maps coordinates expressed in the (column) basisAto the basisB, and extracts the ZXZ Euler angles(α, β, γ)in radians.- Parameters:
A (np.ndarray) – 3×3 column-basis matrix of frame A.
B (np.ndarray) – 3×3 column-basis matrix of frame B.
- Returns:
R: 3×3 rotation matrix.alpha(rad): First ZXZ angle.beta(rad): Second ZXZ angle.gamma(rad): Third ZXZ angle.
- Return type:
Notes
Handles the gimbal-lock case
|R[2,2]| = 1explicitly.
- radicalpy.utils.hilbert_to_liouville(H)[source]
Liouvillian for unitary part in the basis of H (H is already in that basis): L[rho] = -i [H, rho] -> vec form: -i (I ⊗ H - H.T ⊗ I)
- radicalpy.utils.infer_bonds(elements, coords, scale=1.2, max_dist=2.0)[source]
Heuristic covalent-bond detection from interatomic distances.
Bonds are inferred when the interatomic distance is less than
scale × (r_i + r_j)using covalent radii (fallback 0.77 Å) and also less thanmax_distto avoid spurious long bonds.- Parameters:
- Returns:
Pairs of atom indices
(i, j)indicating bonds.- Return type:
- radicalpy.utils.is_fast_run()[source]
Is the
--fastparameter set at execution.This function helps examples to be used as tests. By running the example with the
--fastoption, a faster version of main can be called (e.g., by setting fewer number of time steps etc.).
- radicalpy.utils.mK_to_GHz(mK)[source]
Convert a temperature-like energy from mK to GHz using h ν = k_B T.
Inverse of
convert_GHz_to_mK().
- radicalpy.utils.mK_to_meV(mK)[source]
Convert an energy from mK to meV.
Uses 1 mK = 8.61740×10⁻⁵ meV.
- radicalpy.utils.make_resonance_sticks(*, bins=1000, freq=9373000000.0, gval=2.0023, hfcH=(), hfcN=())[source]
Stick-field/intensity generator.
- Returns:
fields_gauss ((M,) ndarray) – Non-zero histogram bin centers, in GAUSS.
intensities ((M,) ndarray) – Corresponding normalized intensities (sum = 1).
- radicalpy.utils.mary_lorentzian(mod_signal, lfe_magnitude)[source]
Lorentzian MARY spectral shape.
Used for brute force modulated MARY simulations.
- Parameters:
mod_signal (np.ndarray) – The modulated signal.
lfe_magnitude (float) – The magnitude of your low field effect (G).
- Returns:
The modulated MARY signal.
- Return type:
np.ndarray
- radicalpy.utils.meV_to_GHz(meV)[source]
Convert an energy from meV to GHz.
Uses 1 meV = 1 / (4.1357×10⁻³) GHz.
- radicalpy.utils.meV_to_J(meV)[source]
Convert an energy from meV to Joules (J).
Uses 1 eV = 1.602176565×10⁻¹⁹ J.
- radicalpy.utils.meV_to_mK(meV)[source]
Convert an energy from meV to mK.
Uses 1 mK = 8.61740×10⁻⁵ meV.
- radicalpy.utils.modulated_signal(timeconstant, theta, frequency)[source]
Modulated MARY signal.
Used for brute force modulated MARY simulations.
- radicalpy.utils.mol_to_plot_arrays(mol)[source]
Return labels, elements, coords[N,3], bonds[(i,j)] from an RDKit Mol with a conformer.
- radicalpy.utils.negativity(rho, dims, subsys, method='tracenorm', logarithmic=False)[source]
Compute the (logarithmic) negativity of a multipartite density matrix.
This is a RadicalPy-compatible reimplementation of QuTiP’s
negativity, using plain NumPy arrays. The partial transpose is taken over the subsystem(s) specified bysubsys.- Parameters:
rho (ndarray of shape (N, N)) – Density matrix in the computational (lab)
prod (basis. Must be square with N =)
dims (Sequence[int]) – Local Hilbert-space dimensions for each subsystem,
[2 (or)
qubits (2] for two)
[2
3
system. (2] for a 2×3×2)
N. (The product must equal)
subsys (int or Sequence[int]) – Index or indices of the subsystem(s)
example (on which to perform the partial transpose. For)
subsys=0
[d0 (on a bipartite)
subsystem. (d1] system transposes the first)
method ({"tracenorm", "eigenvalues"}, optional) –
“tracenorm” (default): use ||ρ^{T_A}||_1 via SVD (stable).
”eigenvalues”: sum of negative eigenvalues of ρ^{T_A}.
logarithmic (bool, optional) – If True, return the logarithmic
log2 (negativity)
- Returns:
Negativity (or logarithmic negativity if
logarithmic=True).- Return type:
- radicalpy.utils.nmr_chemical_shift_imaginary_modulation(freq_hz, t)[source]
Chemical-shift modulation (imag): sin(2π f t).
- Return type:
- radicalpy.utils.nmr_chemical_shift_real_modulation(freq_hz, t)[source]
Chemical-shift modulation (real): cos(2π f t).
- Return type:
- radicalpy.utils.nmr_scalar_coupling_modulation(j_hz, t, mult_minus_one)[source]
J-modulation: cos(π J t) ** (mult - 1).
- Return type:
- radicalpy.utils.parse_label_xyz_txt(path)[source]
Parse a simple labeled XYZ-like text file.
Expected format per non-comment line:
<label> <x> <y> <z>with whitespace separation. Lines starting with#are ignored.- Parameters:
path (str | Path) – Path to the labeled coordinate file.
- Returns:
labels: Raw labels from file.elements: Guessed elements from label alphabetic prefix.coords: Cartesian coordinates, shape(N, 3)(Å).
- Return type:
- Raises:
ValueError – If a line cannot be parsed into 4 fields.
- radicalpy.utils.parse_pdb(path, use_rdkit_bonds=False, label_scheme='atom')[source]
Load a PDB and return plotting arrays (labels, elements, coords, bonds).
Builds coordinates from an existing conformer or embeds one if missing. Optionally uses RDKit’s bond topology.
- Parameters:
path (str | Path) – Path to a
.pdbfile.use_rdkit_bonds (bool) – If True, bonds are taken from RDKit connectivity; otherwise an empty list is returned.
label_scheme (str) – One of
{"atom","res_atom","atom_serial","chain_res_atom"}passed topdb_label().
- Returns:
labels: Per-atom labels.elements: Atomic symbols.coords: Cartesian coordinates, shape(N, 3)(Å).bonds: Pairs of atom indices (optional ifuse_rdkit_bonds=False).
- Return type:
- radicalpy.utils.parse_xyz(path)[source]
Parse a standard XYZ file (single frame).
The file must contain at least the header line with atom count, a comment line, and
Nsubsequent atom lines.- Parameters:
path (str | Path) – Path to the
.xyzfile.- Returns:
labels: Auto-generated labels<element><index>.elements: Atomic symbols.coords: Cartesian coordinates, shape(N, 3)(Å).
- Return type:
- Raises:
ValueError – If the file is too short or the atom count does not match.
- radicalpy.utils.pdb_label(atom, scheme='chain_res_atom')[source]
Create a human-readable atom label from PDB residue info.
- Parameters:
atom (rdkit.Chem.Atom) – RDKit atom with PDB residue info.
scheme (str) – Labeling scheme: -
"atom"→ atom name or element symbol. -"res_atom"→RESN<resid>:ATOM. -"atom_serial"→ATOM(serial). -"chain_res_atom"(default) →CHAIN:RESN<resid>:ATOM.
- Returns:
Formatted label; falls back to element symbol if fields are missing.
- Return type:
- radicalpy.utils.purity(rho)[source]
Calculate the purity of a density matrix.
The purity is defined as \(P(\rho) = \operatorname{Tr}(\rho^2)\). It quantifies how mixed a quantum state is: pure states have \(P = 1\), while a maximally mixed state in a Hilbert space of dimension \(d\) has \(P = 1/d\).
- Parameters:
rho (ndarray of shape (N, N)) – Density matrix of the quantum state.
and (The matrix is expected to be square)
trace (with unit)
inside (although these conditions are not enforced)
function. (the)
- Returns:
Purity of the state, computed as
real(trace(rho @ rho)).- Return type:
- radicalpy.utils.read_lines_utf8_or_utf16le(path)[source]
Read text file as UTF-8 (with BOM) or UTF-16LE, returning lines.
Tries UTF-8 (BOM-aware) first, then UTF-16LE, which is common in ORCA outputs on Windows.
- Parameters:
path (Path) – File path.
- Returns:
Lines of the decoded file (including newlines).
- Return type:
- Raises:
UnicodeError – If the file is neither valid UTF-8 nor UTF-16LE.
- radicalpy.utils.read_orca_hyperfine(path, version=6)[source]
Read ORCA output file.
- Parameters:
path (Path) – The path to the ORCA output file. Both .out and .property.txt are supported.
- Returns:
The list of nucleus indices, isotopes, and HFC matrices. Note that indices starts from 0 (same as ORCA).
- Return type:
Examples
>>> import radicalpy as rp >>> indices, isotopes, hfc_matrices = rp.utils.read_orca("orca.out") # or rp.utils.read_orca("orca.property.txt") >>> nuclei = [rp.data.Nucleus.fromisotope(isotope, hfc_matrix.tolist()) for isotope, hfc_matrix in zip(isotopes, hfc_matrices)] >>> molecule = rp.simulation.Molecule("mol1", nuclei) Molecule: mol1 Nuclei: 14N(19337792.0, 3, 1.017 <anisotropic available>) 1H(267522187.44, 2, -2.199 <anisotropic available>) 1H(267522187.44, 2, -2.198 <anisotropic available>) Radical: E(-176085963023.0, 2, 0.0 <anisotropic not available>)
- radicalpy.utils.read_trajectory_files(path, scale=1e-10)[source]
Load and concatenate text trajectory files in a directory.
Each file under
pathis read withnumpy.genfromtxtand concatenated along the first axis; the result is multiplied byscale(default converts Å → m if files are in Å).- Parameters:
path (Path) – Directory containing trajectory fragments.
scale (float) – Multiplicative scaling factor applied post-concat.
- Returns:
Concatenated (and scaled) trajectory array.
- Return type:
np.ndarray
- radicalpy.utils.reference_signal(timeconstant, harmonic, theta, frequency)[source]
Modulated MARY reference signal.
Used for brute force modulated MARY simulations.
- radicalpy.utils.rodrigues_rotation(v, k, theta)[source]
Rotate vector(s)
vabout axiskby angletheta(Rodrigues’ formula).Supports arrays of stacked row vectors
vwith shape(N, 3)or column-major layout(3, N). The axiskis normalised internally.- Parameters:
v (np.ndarray) – Vectors to rotate, shape
(N, 3)or(3, N).k (array-like) – Rotation axis (length-3).
theta (float) – Rotation angle (rad).
- Returns:
Rotated vectors with the same shape as
v.- Return type:
np.ndarray
- radicalpy.utils.rotate_axes(A, x, y, z)[source]
Apply rotation matrix
Ato a basis(x, y, z).- Parameters:
A (np.ndarray) – 3×3 rotation matrix.
x (np.ndarray) – Input basis unit vectors (length-3).
y (np.ndarray) – Input basis unit vectors (length-3).
z (np.ndarray) – Input basis unit vectors (length-3).
- Returns:
Rotated basis vectors
(x', y', z').- Return type:
(np.ndarray, np.ndarray, np.ndarray)
- radicalpy.utils.s_t0_omega(deltag, B0, hfc_star, onuc_all)[source]
Compute the two radical pair frequencies ω+ and ω- (in rad/s) for S-T0 mixing.
- Parameters:
- Returns:
The ω+ frequency (rad/s). omega_minus (float): The ω- frequency (rad/s).
- Return type:
omega_plus (float)
- radicalpy.utils.smiles_to_3d(smiles, add_h=True, opt='mmff')[source]
Create a 3D-embedded RDKit Mol from SMILES (ETKDG + optional MMFF/UFF).
- radicalpy.utils.spectral_density(omega, tau_c)[source]
Frequency at which the motion of the particle exists.
- radicalpy.utils.spherical_average(product_yield, theta, phi)[source]
Spherical average of anisotropic product yields.
- Parameters:
product_yield (np.ndarray) – The anisotropic product yields.
theta (np.ndarray) – The angles theta by which the anisotropic product yields were calculated.
phi (np.ndarray) – The angles phi by which the anisotropic product yields were calculated.
- Returns:
- The spherical average of the anisotropic product
yields.
- Return type:
- radicalpy.utils.spherical_to_cartesian(theta, phi)[source]
Spherical coordinates to Cartesian coordinates.
- radicalpy.utils.spin_character(state, character)[source]
Compute the “character” (overlap/content) of a spin state with respect to a chosen subspace, e.g. singlet, triplet, quintet, …
The subspace is specified by a list of kets (column vectors). The function builds the projector onto the span of those kets and evaluates the expectation value of that projector in the given state.
The state may be given either as a pure state vector
|ψ⟩or as a density matrixρ:If
stateis a 1-D array, the function returns \(⟨ψ|P|ψ⟩\), i.e. the probability that|ψ⟩lies in the specified subspace.If
stateis a 2-D array, the function returns \(\mathrm{Tr}(P ρ)\), i.e. the population of the subspace in the mixed stateρ.
- Parameters:
state (ndarray) – Either a state vector of shape
(N,)or a density`` (matrix of shape)
character. (the kets in)
character (list of ndarray) – List of basis kets (each of shape
(N,))measured. (that span the subspace whose character is to be)
- Returns:
- The subspace weight / character, in the range
[0, 1]for normalised inputs.
- The subspace weight / character, in the range
- Return type:
- radicalpy.utils.von_neumann_entropy(rho)[source]
Compute the von Neumann entropy of a quantum state.
The von Neumann entropy is defined as \(S(\rho) = -\mathrm{Tr}\,(\rho\,\log \rho)\), which, in the eigenbasis of \(\rho\), reduces to \(S = -\sum_i \lambda_i \log \lambda_i\), where \(\{\lambda_i\}\) are the eigenvalues of \(\rho\). This implementation uses the natural logarithm, so the entropy is returned in nats.
- Parameters:
rho (ndarray of shape (N, N)) – Density matrix.
states (For physical)
Hermitian (rho should be)
:param : :param positive semidefinite: :type positive semidefinite:
Tr(rho)=1:param and trace-normalised: :type and trace-normalised:Tr(rho)=1:param the function does not explicitly enforce these constraints.:- Returns:
The von Neumann entropy \(S(\rho)\) in nats.
- Return type:
- radicalpy.utils.write_orca_from_pdb(pdb_path, out_dir, *, title='Tryptophan radical cation', charge=1, multiplicity=2, opt_filename=None, opt_method='RHF', opt_basis='def2-TZVP', opt_flags=('NormalPrint', 'NormalSCF'), scf_maxiter=125, scf_cnvdiis=True, scf_cnvsoscf=True, output_print_basis=2, output_print_mos=1, xyz_sidecar=None, epr_filename=None, epr_method='B3LYP', epr_basis='EPR-II', epr_autoaux=True, epr_gtensor=True, epr_nuclei_elements=('H', 'N', 'C', 'O'), epr_props=('AISO', 'ADIP'), pal_nprocs=None)[source]
Create two ORCA inputs that match your attached templates while retaining customisation knobs (methods, basis, SCF/output options, EPRNMR content).
Returns a list with paths to (opt_input, epr_input, xyz_file).
- radicalpy.utils.write_pdb(mol, path)[source]
Write the current conformer to a PDB file.
- Parameters:
mol (rdkit.Chem.Mol) – Molecule to serialise.
path (str | Path) – Destination
.pdbfilepath.
- Returns:
Prints a confirmation line with the saved path.
- radicalpy.utils.write_sdf(mol, path)[source]
Write the molecule’s current conformer to an SDF file (one record).
Saves the active conformer of an RDKit
Molas a single SDF record, preserving topology (bonds, charges, stereochemistry) and 3D coordinates.- Parameters:
mol – RDKit
Chem.Molinstance containing the conformer to export.path – Destination file path for the
sdffile.
Notes
Creates the file or overwrites it if it already exists.
Prints a short confirmation message on success.
- radicalpy.utils.write_xyz(mol, path)[source]
Write the current conformer to an XYZ file (single frame).
- Parameters:
mol (rdkit.Chem.Mol) – Molecule to serialise.
path (str | Path) – Destination
.xyzfilepath.
Notes
Writes a minimal XYZ with a generated comment line.
- radicalpy.utils.yield_anisotropy(product_yield, theta, phi)[source]
Calculate the yield anisotropy.
- Parameters:
product_yield (np.ndarray) – The anisotropic product yields.
theta (np.ndarray) – The angles theta by which the anisotropic product yields were calculated.
phi (np.ndarray) – The angles phi by which the anisotropic product yields were calculated.
- Returns:
delta_phi (float): Maximum yield - minimum yield.
gamma (float): delta_phi / spherical average.
- Return type:
Module contents
Radicalpy package root.