Source code for sim.hamiltonian.terms.zfs
# ==============================================================================
# QUSIM - Quantum Simulator for NV Centers
# Leon Kaiser, MSQC Goethe University, Frankfurt, Germany
# https://msqc.cgi-host6.rz.uni-frankfurt.de
# I.kaiser[at]em.uni-frankfurt.de
#
# This software is provided for scientific and educational purposes.
# Free to use, modify, and distribute with attribution.
# ==============================================================================
"""
Zero-Field Splitting (ZFS) term for the NV center Hamiltonian.
Physical Background
-------------------
The NV center has two unpaired electrons forming a triplet state
(S=1). The spin-spin interaction between these electrons leads to
Zero-Field Splitting (ZFS) - an energy splitting even without an
external magnetic field.
The ZFS Hamiltonian is:
H_ZFS = D·Sz² + E·(Sx² - Sy²)
where:
- D ≈ 2.87 GHz: Axial parameter (splitting ms=0 ↔ ms=±1)
- E: Transverse parameter (strain-induced, lifts ms=±1 degeneracy)
Energy Levels
-------------
Without E-term (E=0):
- E(ms=0) = 0
- E(ms=±1) = D = 2.87 GHz (degenerate)
With E-term (E≠0):
- E(ms=0) = 0
- E(ms=+1) = D + E
- E(ms=-1) = D - E
→ Splitting of 2E between ms=±1
Typical Values
--------------
- D_GS = 2.87 GHz (ground state at room temperature)
- D_ES = 1.42 GHz (excited state)
- E = 0 to ~10 MHz (depends on strain/defects)
Temperature Dependence
----------------------
D(T) ≈ D₀ + α·(T - 300K) with α ≈ -74 kHz/K
References
----------
[1] Doherty et al., Physics Reports 528, 1-45 (2013)
[2] Maze et al., New J. Phys. 13, 025025 (2011)
"""
import numpy as np
from typing import Optional
from .base import HamiltonianTerm
from ...core.operators import spin1_operators, extend_to_18x18
from ...core.constants import GHZ
[docs]
class ZFS(HamiltonianTerm):
"""
Zero-Field Splitting Hamiltonian term.
Implements H_ZFS = D·Sz² + E·(Sx² - Sy²)
Parameters
----------
D : float
Axial ZFS parameter in GHz.
Default: 2.87 GHz (NV ground state at room temperature)
E : float
Transverse ZFS parameter in GHz.
Default: 0 (no strain splitting)
name : str, optional
Custom name for this term
Attributes
----------
D : float
Axial parameter in GHz
E : float
Transverse parameter in GHz
Examples
--------
Standard NV center (no strain):
>>> zfs = ZFS(D=2.87)
>>> H = zfs.build()
>>> eigvals = np.linalg.eigvalsh(H) / (2*np.pi*1e9) # in GHz
>>> np.unique(np.round(eigvals, 2))
array([0. , 2.87])
With strain splitting (E=5 MHz):
>>> zfs = ZFS(D=2.87, E=0.005)
>>> H = zfs.build()
>>> eigvals = np.linalg.eigvalsh(H) / (2*np.pi*1e9)
>>> # ms=±1 are now split at 2.865 and 2.875 GHz
Notes
-----
The matrix is 18×18, but ZFS only acts on the electron spin.
The 6-fold degeneracy at E(ms=0)=0 comes from:
- 2× g/e states
- 3× nuclear spin mI states
The 12-fold degeneracy at E(ms=±1)=D comes from:
- 2× ms=+1 and ms=-1
- 2× g/e states
- 3× nuclear spin mI states
"""
[docs]
def __init__(
self,
D: float = 2.87,
E: float = 0.0,
name: Optional[str] = None
):
super().__init__(name=name)
# Store parameters (in GHz, as given by user)
self.D = D
self.E = E
# Pre-compute spin operators (3×3)
self._ops = spin1_operators()
# Cache for 18×18 matrix (since time-independent)
self._H_18_cache: Optional[np.ndarray] = None
@property
def is_time_dependent(self) -> bool:
"""ZFS is time-independent (without external modulation)."""
return False
def _build_3x3(self) -> np.ndarray:
"""
Build the 3×3 ZFS matrix in the electron spin subspace.
The formula is:
H = D·Sz² + E·(Sx² - Sy²)
In the {|+1>, |0>, |-1>} basis, Sz² = diag(1, 0, 1),
so D·Sz² gives diagonal elements (D, 0, D).
The E-term mixes |+1> and |-1> and creates off-diagonal elements.
Returns
-------
np.ndarray
3×3 complex Hermitian matrix in rad/s
"""
Sx = self._ops.Sx
Sy = self._ops.Sy
Sz = self._ops.Sz
# Convert GHz -> rad/s
D_rads = self.D * GHZ
E_rads = self.E * GHZ
# H = D·Sz² + E·(Sx² - Sy²)
Sz_squared = Sz @ Sz # = diag(1, 0, 1)
Sx_sq_minus_Sy_sq = Sx @ Sx - Sy @ Sy
H_zfs = D_rads * Sz_squared + E_rads * Sx_sq_minus_Sy_sq
return H_zfs
[docs]
def build(self, t: float = 0.0) -> np.ndarray:
"""
Build the 18×18 ZFS matrix.
Parameters
----------
t : float
Time in seconds (ignored, since time-independent)
Returns
-------
np.ndarray
18×18 Hermitian matrix in rad/s
"""
# Use cache since time-independent
if self._H_18_cache is None:
H_3x3 = self._build_3x3()
self._H_18_cache = extend_to_18x18(H_3x3)
return self._H_18_cache.copy()
def __repr__(self) -> str:
if self.E == 0:
return f"ZFS(D={self.D} GHz)"
else:
return f"ZFS(D={self.D} GHz, E={self.E} GHz)"