1313from scipy .interpolate import interp1d
1414
1515from .utilities import find_nearest
16+ from .units import ANGSTROM_TO_M
1617
1718
1819def compute_relaxation_rates (f , J , K , isotropic ):
@@ -23,11 +24,12 @@ def compute_relaxation_rates(f, J, K, isotropic):
2324 Parameters
2425 ----------
2526 f : np.ndarray
26- Frequency array.
27+ Frequency array, in MHz .
2728 J : np.ndarray
28- Spectral density, shape (dim, n_freqs); dim=1 if isotropic, else 3.
29+ Spectral density, shape (dim, n_freqs), in Angstrom^-6 * s;
30+ dim=1 if isotropic, else 3.
2931 K : float
30- Dipolar relaxation prefactor.
32+ Dipolar relaxation prefactor, in m^6 * s^-2 (SI) .
3133 isotropic : bool
3234 Whether to use the isotropic (dim=1) or anisotropic (dim=3) formula.
3335
@@ -36,21 +38,21 @@ def compute_relaxation_rates(f, J, K, isotropic):
3638 R1, R2 : np.ndarray
3739 Relaxation rates on the same frequency grid as f.
3840 """
39- prefactor = K / cst . angstrom ** 6
40- idx0 = find_nearest (f , 0.0 )
41- J0 = J [0 ]
41+ prefactor = K / ANGSTROM_TO_M ** 6 # Angstrom^6 * s^-2 (K rescaled m^6 -> Angstrom^6 to match J)
42+ idx0 = find_nearest (f , 0.0 ) # index, dimensionless
43+ J0 = J [0 ] # Angstrom^-6 * s
4244
4345 if isotropic :
44- J02 = interp1d (f , J0 , fill_value = "extrapolate" )(2 * f )
45- R1 = prefactor * (J0 + 4 * J02 ) / 6
46- R2 = prefactor * (3 / 2 * J0 [idx0 ] + 5 / 2 * J0 + J02 ) / 6
46+ J02 = interp1d (f , J0 , fill_value = "extrapolate" )(2 * f ) # Angstrom^-6 * s
47+ R1 = prefactor * (J0 + 4 * J02 ) / 6 # s^-1
48+ R2 = prefactor * (3 / 2 * J0 [idx0 ] + 5 / 2 * J0 + J02 ) / 6 # s^-1
4749 else :
48- J1 = J [1 ]
49- J2 = interp1d (f , J [2 ], fill_value = "extrapolate" )(2 * f )
50- R1 = prefactor * (J1 + J2 )
51- R2 = prefactor * (1 / 4 ) * (J0 [idx0 ] + 10 * J1 + J2 )
50+ J1 = J [1 ] # Angstrom^-6 * s
51+ J2 = interp1d (f , J [2 ], fill_value = "extrapolate" )(2 * f ) # Angstrom^-6 * s
52+ R1 = prefactor * (J1 + J2 ) # s^-1
53+ R2 = prefactor * (1 / 4 ) * (J0 [idx0 ] + 10 * J1 + J2 ) # s^-1
5254
53- return R1 , R2
55+ return R1 , R2 # s^-1, s^-1
5456
5557def compute_relaxation_times (f , R1 , R2 , target_frequency = None ):
5658 """Compute T1 and T2 relaxation times from relaxation rates R1, R2.
@@ -62,27 +64,27 @@ def compute_relaxation_times(f, R1, R2, target_frequency=None):
6264 Parameters
6365 ----------
6466 f : np.ndarray
65- Frequency array.
67+ Frequency array, in MHz .
6668 R1 : np.ndarray
67- Longitudinal relaxation rate, same shape as f.
69+ Longitudinal relaxation rate, in s^-1, same shape as f.
6870 R2 : np.ndarray
69- Transverse relaxation rate, same shape as f.
71+ Transverse relaxation rate, in s^-1, same shape as f.
7072 target_frequency : float, optional
71- Frequency at which to evaluate T1/T2. If None, f=0 is used.
73+ Frequency at which to evaluate T1/T2, in MHz . If None, f=0 is used.
7274
7375 Returns
7476 -------
7577 T1, T2 : float
76- Relaxation times. np.inf if the corresponding rate is ~0.
78+ Relaxation times, in s . np.inf if the corresponding rate is ~0.
7779 """
78- target = 0.0 if target_frequency is None else target_frequency
79- idx = find_nearest (f , target )
80+ target = 0.0 if target_frequency is None else target_frequency # MHz
81+ idx = find_nearest (f , target ) # index, dimensionless
8082
81- R1_val = R1 [idx ]
82- R2_val = R2 [idx ]
83+ R1_val = R1 [idx ] # s^-1
84+ R2_val = R2 [idx ] # s^-1
8385
84- eps = 1e-20
85- T1 = np .inf if np .isclose (R1_val , 0.0 ) else 1.0 / (R1_val + eps )
86- T2 = np .inf if np .isclose (R2_val , 0.0 ) else 1.0 / (R2_val + eps )
86+ eps = 1e-20 # s^-1 (safety floor, sized for R1/R2 ~ 0.1-1e4 s^-1)
87+ T1 = np .inf if np .isclose (R1_val , 0.0 ) else 1.0 / (R1_val + eps ) # s
88+ T2 = np .inf if np .isclose (R2_val , 0.0 ) else 1.0 / (R2_val + eps ) # s
8789
88- return T1 , T2
90+ return T1 , T2 # s, s
0 commit comments