Skip to content

Commit 77315ae

Browse files
authored
Merge pull request #206 from Spin-Chemistry-Labs/205-add-soc-and-nqi-hamiltonians
205 add soc and nqi hamiltonians
2 parents 0570a5d + cd92f15 commit 77315ae

9 files changed

Lines changed: 803 additions & 860 deletions

File tree

examples/bloch-redfield.py

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,46 +14,37 @@
1414
def main():
1515
m1 = Molecule.fromdb("flavin_anion", ["N5"])
1616
m2 = Molecule.fromdb("tryptophan_cation", [])
17-
simH = HilbertSimulation([m1, m2])
18-
sim = LiouvilleSimulation([m1, m2])
19-
H = simH.total_hamiltonian(B0=0, J=0, D=0)
17+
sim = HilbertSimulation([m1, m2])
18+
H = sim.total_hamiltonian(B0=0, J=0, D=0)
2019

21-
sigmax = simH.spin_operator(0, "x") + simH.spin_operator(1, "x")
20+
rho0 = sim.projection_operator(State.SINGLET)
21+
obs = sim.projection_operator(State.SINGLET)
2222

23-
S = 3e6
24-
BRs = sigmax
25-
blochredfield = sim.bloch_redfield_liouvillian(
26-
H, channels=[(BRs, S)], secular=True, secular_cutoff=0.01
27-
)
23+
sigmax = sim.spin_operator(0, "x") + sim.spin_operator(1, "x")
24+
sigmay = sim.spin_operator(0, "y") + sim.spin_operator(1, "y")
25+
sigmaz = sim.spin_operator(0, "z") + sim.spin_operator(1, "z")
2826

29-
dB = 0.5
30-
B0 = np.arange(0.0, 20.0 + 1e-9, dB)
31-
time = np.arange(0, 3e-6, 10e-9)
32-
kinetics = [Haberkorn(3e6, State.SINGLET), HaberkornFree(1e6)]
33-
relaxations = [SingletTripletDephasing(1e7)]
27+
Sxyz = sigmax + sigmay + sigmaz
3428

35-
init_state = State.SINGLET
36-
obs_state = State.SINGLET
29+
def S(omega: float, tau_c: float) -> float:
30+
omega = float(omega)
31+
return tau_c / (1.0 + (omega * tau_c) * (omega * tau_c))
3732

38-
sim.apply_liouville_hamiltonian_modifiers(blochredfield, kinetics + relaxations)
39-
rhos = magnetic_field_loop(sim, init_state, time, blochredfield, B0, B_axis="z")
40-
product_probabilities = sim.product_probability(obs_state, rhos)
33+
tau_c = 1e7
34+
NPS = lambda omega: S(omega, tau_c)
4135

42-
sim.apply_hilbert_kinetics(time, product_probabilities, kinetics)
43-
k = kinetics[0].rate_constant if kinetics else 1.0
44-
product_yields, product_yield_sums = sim.product_yield(
45-
product_probabilities, time, k
46-
)
36+
time = np.arange(0, 5e-6, 1e-9)
4737

48-
x = time * 1e6
49-
n = 0
38+
L = sim.bloch_redfield_time_evolution(
39+
H, rho0, time, bath=[Sxyz], noise=[NPS], obs=[obs]
40+
)
41+
L_result = L["expect"][0] / np.trace(rho0)
5042

5143
fig = plt.figure(1)
52-
plt.plot(x, product_probabilities[n, :], linewidth=2, label=r"$P_i(t)$")
53-
plt.fill_between(x, product_yields[n, :], alpha=0.2, label=r"$\Phi_i$")
44+
plt.plot(time * 1e6, L_result)
5445
plt.xlabel("Time / $\mu s$", size=14)
5546
plt.ylabel("Probability", size=14)
56-
# plt.ylim([0, 1])
47+
plt.ylim([0, 1])
5748
plt.legend(fontsize=14)
5849
fig.set_size_inches([7, 4])
5950
plt.show()

examples/energy_level.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,14 @@
88

99

1010
def main():
11-
flavin = rp.simulation.Molecule.fromdb("flavin_anion", ["H25"])
11+
flavin = rp.simulation.Molecule.fromdb("flavin_anion", [])
1212
Z = rp.simulation.Molecule("Z")
1313
sim = rp.simulation.HilbertSimulation([flavin, Z])
14-
H = sim.total_hamiltonian(B0=1, D=0, J=0)
1514

16-
eigval = np.linalg.eigh(H)
17-
E = np.real(eigval[0]) # 0 = eigenvalues, 1 = eigenvectors
18-
19-
rp.plot.energy_levels(sim, B=np.arange(0.01, 1, 0.01), J=0, D=0)
20-
# plt.show()
21-
path = __file__[:-3] + f"_{0}.png"
22-
plt.savefig(path)
15+
rp.plot.energy_levels(sim, B=np.arange(0.01, 1, 0.01), J=-0.05, D=0)
16+
plt.show()
17+
# path = __file__[:-3] + f"_{0}.png"
18+
# plt.savefig(path)
2319

2420

2521
if __name__ == "__main__":

radicalpy/classical.py

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,58 +7,55 @@
77
sampling.
88
99
10-
Contents
11-
--------
10+
Contents:
1211
13-
- Diffusion:
12+
- Diffusion:
1413
15-
- get_delta_r(D, dt): RMS relative displacement √(6 D Δt).
14+
- get_delta_r(D, dt): RMS relative displacement √(6 D Δt).
1615
17-
- LaTeX utilities:
16+
- LaTeX utilities:
1817
19-
- latexify(rate_equations): Build d[Xi]/dt = … strings from a rate map.
18+
- latexify(rate_equations): Build d[Xi]/dt = … strings from a rate map.
2019
21-
- latex_eqlist_to_align(eqs): Wrap equations in an `align*` environment.
20+
- latex_eqlist_to_align(eqs): Wrap equations in an `align*` environment.
2221
23-
- Kinetics:
22+
- Kinetics:
2423
25-
- Rate: Numeric value + LaTeX label with operator overloading that carries
26-
symbolic expressions through +, −, ×, ÷.
24+
- Rate: Numeric value + LaTeX label with operator overloading that carries
25+
symbolic expressions through +, −, ×, ÷.
2726
28-
- RateEquations: Sparse representation and time evolution (matrix exponential)
29-
for first-order networks; returns EquationRateResult for easy access.
27+
- RateEquations: Sparse representation and time evolution (matrix exponential)
28+
for first-order networks; returns EquationRateResult for easy access.
3029
31-
- EquationRateResult: Convenience accessor to sum populations of one or more
32-
states over time.
30+
- EquationRateResult: Convenience accessor to sum populations of one or more
31+
states over time.
3332
34-
- Visualization:
33+
- Visualization:
3534
36-
- reaction_scheme(path, rate_equations): Generate a LaTeX (dot2tex) diagram
37-
of the reaction network.
35+
- reaction_scheme(path, rate_equations): Generate a LaTeX (dot2tex) diagram
36+
of the reaction network.
3837
39-
- Random sampling:
38+
- Random sampling:
4039
41-
- random_theta_phi(n): Uniform sampling on the unit sphere.
40+
- random_theta_phi(n): Uniform sampling on the unit sphere.
4241
43-
- randomwalk_3d(...): Monte-Carlo 3D random walk with min/ max-distance
44-
constraints (solution or spherical microreactor).
42+
- randomwalk_3d(...): Monte-Carlo 3D random walk with min/ max-distance
43+
constraints (solution or spherical microreactor).
4544
4645
47-
Key conventions
48-
---------------
46+
Key conventions:
4947
50-
- Rate maps use: sink_state -> { source_state: Rate|float, ... }
51-
The resulting sparse matrix M stores rates at (sink, source), so dP/dt = M P.
48+
- Rate maps use: sink_state -> { source_state: Rate|float, ... }
49+
The resulting sparse matrix M stores rates at (sink, source), so dP/dt = M P.
5250
53-
- Time evolution assumes uniform spacing in `time` and advances via the matrix
54-
exponential of the rate matrix over Δt.
51+
- Time evolution assumes uniform spacing in `time` and advances via the matrix
52+
exponential of the rate matrix over Δt.
5553
5654
57-
Dependencies
58-
------------
55+
Dependencies:
5956
60-
Relies on NumPy and SciPy (sparse) for numerics, graphviz + dot2tex for
61-
LaTeX diagram generation.
57+
Relies on NumPy and SciPy (sparse) for numerics, graphviz + dot2tex for
58+
LaTeX diagram generation.
6259
6360
"""
6461

radicalpy/data.py

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,52 @@
88
and multiplicities. It also includes accessors for bundled reference data
99
and convenience builders for frequently used composite objects.
1010
11-
Contents
12-
--------
13-
Conversions
14-
- :func:`spin_to_multiplicity` – convert spin quantum number ``S`` to
15-
multiplicity ``2S+1`` (validates half‐integer/integer input).
16-
- :func:`multiplicity_to_spin` – invert the mapping, returning
17-
``S = (M-1)/2``.
18-
19-
Data access
20-
- :func:`get_data` – locate packaged data files (e.g. JSON databases).
21-
22-
Core data structures
23-
- :class:`Isotope` – look up an isotope’s spin multiplicity and
24-
magnetogyric ratio from the bundled database (CODATA-style values),
25-
with helpers like :py:meth:`Isotope.available`.
26-
- :class:`Hfc` – hyperfine coupling container that supports either an
27-
isotropic scalar value or a full 3×3 anisotropic tensor, and exposes
28-
:py:meth:`Hfc.isotropic` and :py:meth:`Hfc.anisotropic`.
29-
- :class:`Nucleus` – a nucleus within a molecule, defined by its
30-
magnetogyric ratio, spin multiplicity, and hyperfine couplings; also
31-
provides spin (Pauli) operator matrices via :py:meth:`Nucleus.pauli`.
32-
- :class:`FuseNucleus` – an effective nucleus formed by fusing several
33-
identical nuclei into a direct-sum representation to reduce Hilbert-space
34-
dimension; includes utilities for validation and operator construction.
35-
- :class:`Molecule` – collection of nuclei plus a radical (electron‐like
36-
spin), with constructors for database-backed molecules
37-
(:py:meth:`Molecule.fromdb`, :py:meth:`Molecule.all_nuclei`) and
38-
ad-hoc assemblies (:py:meth:`Molecule.fromisotopes`).
39-
- :class:`Triplet` – convenience molecule containing a single S=1
40-
(multiplicity 3) radical and no nuclei.
41-
42-
Units & conventions
43-
-------------------
44-
- Magnetogyric ratios are commonly expressed as ``rad/s/T`` in the database;
45-
many class properties expose ``gamma_mT`` (``rad/s/mT``) for convenience.
46-
- Hyperfine couplings (HFCs) are in mT. When a 3×3 tensor is provided,
47-
the isotropic value is computed as ``trace(D)/3``.
48-
- Spin operators follow the usual convention with raising (``p``),
49-
lowering (``m``), and Cartesian components (``x``, ``y``, ``z``).
50-
51-
Error handling
52-
--------------
53-
- :func:`spin_to_multiplicity` validates that ``S`` is integer or half-integer.
54-
- :class:`Isotope` raises ``ValueError`` for unknown symbols and provides
55-
:py:meth:`Isotope.available` to enumerate valid options.
56-
- :class:`Hfc.anisotropic` raises ``ValueError`` when no tensor is available.
11+
Contents:
12+
13+
Conversions
14+
- :func:`spin_to_multiplicity` – convert spin quantum number ``S`` to
15+
multiplicity ``2S+1`` (validates half‐integer/integer input).
16+
- :func:`multiplicity_to_spin` – invert the mapping, returning
17+
``S = (M-1)/2``.
18+
19+
Data access
20+
- :func:`get_data` – locate packaged data files (e.g. JSON databases).
21+
22+
Core data structures
23+
- :class:`Isotope` – look up an isotope’s spin multiplicity and
24+
magnetogyric ratio from the bundled database (CODATA-style values),
25+
with helpers like :py:meth:`Isotope.available`.
26+
- :class:`Hfc` – hyperfine coupling container that supports either an
27+
isotropic scalar value or a full 3×3 anisotropic tensor, and exposes
28+
:py:meth:`Hfc.isotropic` and :py:meth:`Hfc.anisotropic`.
29+
- :class:`Nucleus` – a nucleus within a molecule, defined by its
30+
magnetogyric ratio, spin multiplicity, and hyperfine couplings; also
31+
provides spin (Pauli) operator matrices via :py:meth:`Nucleus.pauli`.
32+
- :class:`FuseNucleus` – an effective nucleus formed by fusing several
33+
identical nuclei into a direct-sum representation to reduce Hilbert-space
34+
dimension; includes utilities for validation and operator construction.
35+
- :class:`Molecule` – collection of nuclei plus a radical (electron‐like
36+
spin), with constructors for database-backed molecules
37+
(:py:meth:`Molecule.fromdb`, :py:meth:`Molecule.all_nuclei`) and
38+
ad-hoc assemblies (:py:meth:`Molecule.fromisotopes`).
39+
- :class:`Triplet` – convenience molecule containing a single S=1
40+
(multiplicity 3) radical and no nuclei.
41+
42+
Units & conventions:
43+
44+
- Magnetogyric ratios are commonly expressed as ``rad/s/T`` in the database;
45+
many class properties expose ``gamma_mT`` (``rad/s/mT``) for convenience.
46+
- Hyperfine couplings (HFCs) are in mT. When a 3×3 tensor is provided,
47+
the isotropic value is computed as ``trace(D)/3``.
48+
- Spin operators follow the usual convention with raising (``p``),
49+
lowering (``m``), and Cartesian components (``x``, ``y``, ``z``).
50+
51+
Error handling:
52+
53+
- :func:`spin_to_multiplicity` validates that ``S`` is integer or half-integer.
54+
- :class:`Isotope` raises ``ValueError`` for unknown symbols and provides
55+
:py:meth:`Isotope.available` to enumerate valid options.
56+
- :class:`Hfc.anisotropic` raises ``ValueError`` when no tensor is available.
5757
"""
5858

5959
from __future__ import annotations

radicalpy/estimations.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,6 @@ def dipolar_interaction_anisotropic_from_dipolar_vector_without_prefactor(
575575

576576
I3 = np.eye(3, dtype=float)
577577
rrT = np.outer(r_m, r_m)
578-
# [(|r|^2) I - 3 r r^T] / |r|^5
579578
T = ((r2 * I3) - 3.0 * rrT) / (rnorm**5)
580579
return T
581580

@@ -626,7 +625,6 @@ def dipolar_interaction_anisotropic_from_dipolar_vector(
626625
dipolar_vector, units=units
627626
)
628627
D = float(dipolar_prefactor) * T_geom
629-
# return complex dtype to match other Hamiltonian builders smoothly
630628
return np.asarray(D, dtype=np.complex128)
631629

632630

@@ -919,7 +917,6 @@ def k_excitation(
919917
pathlength (float): The path length of the sample cell
920918
(m).
921919
922-
923920
Returns:
924921
float: The excitation rate (1/s).
925922
"""

0 commit comments

Comments
 (0)