torchspin 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- torchspin/__init__.py +363 -0
- torchspin/_cardamom_istos.py +327 -0
- torchspin/_cardamom_propagatedm.py +413 -0
- torchspin/_cardamom_utils.py +113 -0
- torchspin/_compile.py +100 -0
- torchspin/_linalg.py +140 -0
- torchspin/angmom.py +742 -0
- torchspin/autograd.py +557 -0
- torchspin/autoguess.py +342 -0
- torchspin/batch.py +225 -0
- torchspin/blochsteady.py +274 -0
- torchspin/cardamom.py +552 -0
- torchspin/chili.py +932 -0
- torchspin/chili_sle.py +1233 -0
- torchspin/constants.py +88 -0
- torchspin/convspec.py +148 -0
- torchspin/ctafft.py +91 -0
- torchspin/curry.py +241 -0
- torchspin/data/FourierSeriesCoefficients.txt +89 -0
- torchspin/data/GaussianCascadeCoefficients.txt +56 -0
- torchspin/data/isotopedata.txt +416 -0
- torchspin/data/spacegroups.txt +637 -0
- torchspin/dataproc.py +361 -0
- torchspin/dipbackground.py +69 -0
- torchspin/dipkernel.py +102 -0
- torchspin/diptensor.py +138 -0
- torchspin/endorfrq.py +198 -0
- torchspin/endorfrq_perturb.py +302 -0
- torchspin/eprload.py +1538 -0
- torchspin/eprsave.py +165 -0
- torchspin/esfit.py +2427 -0
- torchspin/evolve.py +274 -0
- torchspin/ewrls.py +124 -0
- torchspin/excitation.py +107 -0
- torchspin/exciteprofile.py +132 -0
- torchspin/experiment.py +323 -0
- torchspin/exponfit.py +177 -0
- torchspin/fastmotion.py +182 -0
- torchspin/fdaxis.py +57 -0
- torchspin/fitgui.py +350 -0
- torchspin/garlic.py +760 -0
- torchspin/ham.py +145 -0
- torchspin/ham_cf.py +115 -0
- torchspin/ham_ee.py +124 -0
- torchspin/ham_ez.py +119 -0
- torchspin/ham_ezho.py +403 -0
- torchspin/ham_hf.py +122 -0
- torchspin/ham_nn.py +108 -0
- torchspin/ham_nq.py +100 -0
- torchspin/ham_nz.py +136 -0
- torchspin/ham_oz.py +115 -0
- torchspin/ham_so.py +118 -0
- torchspin/ham_zf.py +254 -0
- torchspin/hamsymm.py +723 -0
- torchspin/initstate.py +158 -0
- torchspin/isotopologues.py +296 -0
- torchspin/levels.py +189 -0
- torchspin/levelsplot.py +363 -0
- torchspin/lineshape.py +715 -0
- torchspin/lpsvd.py +219 -0
- torchspin/makespec.py +87 -0
- torchspin/mdhmm.py +573 -0
- torchspin/mdload.py +788 -0
- torchspin/mdtraj2oripot.py +90 -0
- torchspin/ml.py +377 -0
- torchspin/mlpsvd.py +297 -0
- torchspin/nucdata.py +310 -0
- torchspin/nucfrq2d.py +228 -0
- torchspin/orca2torchspin.py +1198 -0
- torchspin/ordering.py +78 -0
- torchspin/oripotentialplot.py +246 -0
- torchspin/orisel.py +176 -0
- torchspin/pepper.py +2019 -0
- torchspin/pepper_autograd.py +613 -0
- torchspin/photoselect.py +174 -0
- torchspin/plegendre.py +131 -0
- torchspin/propint.py +127 -0
- torchspin/pulse.py +652 -0
- torchspin/py.typed +0 -0
- torchspin/rapidscan2spc.py +101 -0
- torchspin/resfields.py +337 -0
- torchspin/resfields_batch.py +446 -0
- torchspin/resfields_eig.py +250 -0
- torchspin/resfields_perturb.py +706 -0
- torchspin/resfreqs_matrix.py +344 -0
- torchspin/resfreqs_perturb.py +299 -0
- torchspin/resonator.py +310 -0
- torchspin/resonatorprofile.py +85 -0
- torchspin/rfmixer.py +147 -0
- torchspin/rotations.py +87 -0
- torchspin/rotutils.py +880 -0
- torchspin/saffron.py +1523 -0
- torchspin/saffron_pathways.py +92 -0
- torchspin/saffron_peaks.py +580 -0
- torchspin/saffron_thyme.py +472 -0
- torchspin/salt.py +680 -0
- torchspin/sigeq.py +71 -0
- torchspin/signalprocessing.py +226 -0
- torchspin/sitetransforms.py +254 -0
- torchspin/sphgrid.py +470 -0
- torchspin/spidyan.py +1508 -0
- torchspin/spinladder.py +161 -0
- torchspin/spinops.py +265 -0
- torchspin/spinsystem.py +1427 -0
- torchspin/stackplot.py +167 -0
- torchspin/stev.py +283 -0
- torchspin/stochtraj_diffusion.py +494 -0
- torchspin/stochtraj_jump.py +195 -0
- torchspin/strainwidth.py +727 -0
- torchspin/transmitter.py +89 -0
- torchspin/utils.py +605 -0
- torchspin-0.3.0.dist-info/METADATA +267 -0
- torchspin-0.3.0.dist-info/RECORD +116 -0
- torchspin-0.3.0.dist-info/WHEEL +5 -0
- torchspin-0.3.0.dist-info/licenses/LICENSE.md +22 -0
- torchspin-0.3.0.dist-info/top_level.txt +1 -0
torchspin/__init__.py
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"""torchspin — PyTorch EPR/ESR spin Hamiltonian engine.
|
|
2
|
+
|
|
3
|
+
A Python/PyTorch port of EasySpin's core physics layer.
|
|
4
|
+
All energies are in **MHz**; magnetic fields in **mT**; Euler angles in **radians**.
|
|
5
|
+
|
|
6
|
+
Quick start::
|
|
7
|
+
|
|
8
|
+
import torch
|
|
9
|
+
from torchspin import SpinSystem, ham
|
|
10
|
+
|
|
11
|
+
# S=1/2 nitroxide-like spin system
|
|
12
|
+
sys = SpinSystem(
|
|
13
|
+
S=[0.5],
|
|
14
|
+
g=[[2.009, 2.006, 2.002]],
|
|
15
|
+
Nucs=['14N'],
|
|
16
|
+
A=[[10.0, 10.0, 95.0]], # MHz
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
B0 = [0.0, 0.0, 340.0] # mT, along z
|
|
20
|
+
H = ham(sys, B0)
|
|
21
|
+
energies = torch.linalg.eigvalsh(H) # MHz
|
|
22
|
+
"""
|
|
23
|
+
from importlib.metadata import version as _pkg_version, PackageNotFoundError as _PkgNotFound
|
|
24
|
+
try:
|
|
25
|
+
__version__ = _pkg_version("torchspin")
|
|
26
|
+
except _PkgNotFound:
|
|
27
|
+
# Editable install during development, or version metadata unavailable.
|
|
28
|
+
__version__ = "0.3.0+dev"
|
|
29
|
+
|
|
30
|
+
from torchspin._compile import maybe_compile, set_compile_enabled, is_compile_available
|
|
31
|
+
from torchspin.autograd import differentiable_spectrum
|
|
32
|
+
from torchspin.batch import batch_pepper, batch_simulate
|
|
33
|
+
from torchspin.cardamom import cardamom, CardamomPar, CardamomOptions, MDInput
|
|
34
|
+
from torchspin.ctafft import ctafft
|
|
35
|
+
from torchspin.ewrls import ewrls
|
|
36
|
+
from torchspin.lpsvd import lpsvd, LPSVDResult
|
|
37
|
+
from torchspin.rapidscan2spc import rapidscan2spc
|
|
38
|
+
from torchspin.stochtraj_diffusion import stochtraj_diffusion, DiffusionPar
|
|
39
|
+
from torchspin.stochtraj_jump import stochtraj_jump, JumpPar
|
|
40
|
+
from torchspin.autoguess import (estimate_parameters, field_from_g, g_from_field,
|
|
41
|
+
print_parameter_report)
|
|
42
|
+
from torchspin.lineshape import (gaussian, lorentzian, voigtian, apowin, addnoise, deriv, lshape)
|
|
43
|
+
from torchspin.dataproc import basecorr, fieldmod, rescaledata
|
|
44
|
+
from torchspin.utils import (datasmooth, equivcouple, equivsplit,
|
|
45
|
+
hilberttrans, larmorfrq, rcfilt, unitconvert,
|
|
46
|
+
mhz2mt, mt2mhz, hsdim, eprconvert)
|
|
47
|
+
from torchspin.nucdata import nucabund, nucspin, nucgval, nucqmom
|
|
48
|
+
from torchspin.blochsteady import blochsteady, BlochOptions
|
|
49
|
+
from torchspin.chili import chili, ChiliOptions
|
|
50
|
+
from torchspin.endorfrq import endorfrq
|
|
51
|
+
from torchspin.salt import salt
|
|
52
|
+
from torchspin.constants import (BMAGN, BOLTZMANN, CLIGHT, EVOLT, GFREE, NMAGN, PLANCK,
|
|
53
|
+
HBAR, AMU, ECHARGE, EMASS, PMASS, NMASS, EPS0, MU0,
|
|
54
|
+
FARADAY, AVOGADRO, BOHRRAD, HARTREE, RYDBERG, GAMMAE, GAMMAN,
|
|
55
|
+
ANGSTROM, DEGREE, BARN, MOLGAS)
|
|
56
|
+
from torchspin.convspec import convspec
|
|
57
|
+
from torchspin.eprload import eprload, eprload_info
|
|
58
|
+
from torchspin.eprsave import eprsave, eprsave_info
|
|
59
|
+
from torchspin.esfit import esfit, FitOptions, FitResult
|
|
60
|
+
from torchspin.experiment import Experiment, Options
|
|
61
|
+
from torchspin.curry import curry
|
|
62
|
+
from torchspin.fastmotion import fastmotion
|
|
63
|
+
from torchspin.garlic import garlic
|
|
64
|
+
from torchspin.ham import ham
|
|
65
|
+
from torchspin.levels import levels
|
|
66
|
+
from torchspin.resfreqs_matrix import resfreqs_matrix
|
|
67
|
+
from torchspin.ham_cf import ham_cf
|
|
68
|
+
from torchspin.ham_ee import ham_ee
|
|
69
|
+
from torchspin.ham_ez import ham_ez
|
|
70
|
+
from torchspin.ham_ezho import ham_ezho
|
|
71
|
+
from torchspin.ham_hf import ham_hf
|
|
72
|
+
from torchspin.ham_nn import ham_nn
|
|
73
|
+
from torchspin.ham_nq import ham_nq
|
|
74
|
+
from torchspin.ham_nz import ham_nz
|
|
75
|
+
from torchspin.ham_oz import ham_oz
|
|
76
|
+
from torchspin.ham_so import ham_so
|
|
77
|
+
from torchspin.ham_zf import ham_zf, zfsframes
|
|
78
|
+
from torchspin.hamsymm import hamsymm
|
|
79
|
+
from torchspin.makespec import makespec
|
|
80
|
+
from torchspin.pepper import pepper
|
|
81
|
+
from torchspin.pepper_autograd import pepper_autograd
|
|
82
|
+
from torchspin.rotations import erot
|
|
83
|
+
from torchspin.rotutils import (eulang, euler2quat, quat2euler, quat2rotmat,
|
|
84
|
+
rotmat2quat, rotaxi2mat, rotmat2axi,
|
|
85
|
+
quatinv, quatmult, quatvecmult,
|
|
86
|
+
vec2ang, ang2vec,
|
|
87
|
+
tensor_cart2sph, tensor_sph2cart, rotateframe)
|
|
88
|
+
from torchspin.diptensor import diptensor
|
|
89
|
+
from torchspin.spinladder import spinladder, SpinManifold
|
|
90
|
+
from torchspin.orisel import orisel, OriselOptions
|
|
91
|
+
from torchspin.spinops import sop, commute
|
|
92
|
+
from torchspin.sphgrid import sphgrid, sphrand
|
|
93
|
+
from torchspin.angmom import (wigner3j, wigner6j, wignerd, spherharm, clebschgordan, cgmatrix,
|
|
94
|
+
isto, isto2stev)
|
|
95
|
+
from torchspin.endorfrq_perturb import endorfrq_perturb
|
|
96
|
+
from torchspin.resfreqs_perturb import resfreqs_perturb
|
|
97
|
+
from torchspin.resfields_eig import resfields_eig, EigOptions
|
|
98
|
+
from torchspin.spidyan import spidyan, SpidyanOptions, SpidyanInfo
|
|
99
|
+
from torchspin.mdload import mdload, MDData, MDInfo, MDOptions
|
|
100
|
+
from torchspin.mdhmm import mdhmm, HMMResult, HMMOptions
|
|
101
|
+
from torchspin.mdtraj2oripot import mdtraj2oripot
|
|
102
|
+
from torchspin.orca2torchspin import orca2torchspin, OrcaData
|
|
103
|
+
from torchspin.mlpsvd import mlpsvd, MLPSVDResult
|
|
104
|
+
from torchspin.oripotentialplot import oripotentialplot
|
|
105
|
+
from torchspin.saffron import saffron, PulseExperiment, SaffronOptions
|
|
106
|
+
from torchspin.signalprocessing import signalprocessing
|
|
107
|
+
from torchspin.saffron_pathways import find_refocusing_pathways
|
|
108
|
+
from torchspin.spinsystem import SpinSystem, nucspinrmv, nucspinkeep, nucspinadd, isotopologues, spinvec
|
|
109
|
+
from torchspin.sigeq import sigeq
|
|
110
|
+
from torchspin.fdaxis import fdaxis
|
|
111
|
+
from torchspin.plegendre import plegendre
|
|
112
|
+
from torchspin.propint import propint
|
|
113
|
+
from torchspin.resonatorprofile import resonatorprofile
|
|
114
|
+
from torchspin.rfmixer import rfmixer
|
|
115
|
+
from torchspin.transmitter import transmitter
|
|
116
|
+
from torchspin.pulse import pulse
|
|
117
|
+
from torchspin.exciteprofile import exciteprofile
|
|
118
|
+
from torchspin.resonator import resonator
|
|
119
|
+
from torchspin.photoselect import photoselect
|
|
120
|
+
from torchspin.levelsplot import levelsplot, LevelsPlotOptions
|
|
121
|
+
from torchspin.stackplot import stackplot
|
|
122
|
+
from torchspin.dipkernel import dipkernel
|
|
123
|
+
from torchspin.dipbackground import dipbackground
|
|
124
|
+
from torchspin.exponfit import exponfit
|
|
125
|
+
from torchspin.nucfrq2d import nucfrq2d
|
|
126
|
+
from torchspin.evolve import evolve
|
|
127
|
+
|
|
128
|
+
__all__ = [
|
|
129
|
+
# Version
|
|
130
|
+
'__version__',
|
|
131
|
+
# Differentiable simulator
|
|
132
|
+
'differentiable_spectrum',
|
|
133
|
+
'pepper_autograd',
|
|
134
|
+
# Core
|
|
135
|
+
'SpinSystem',
|
|
136
|
+
'nucspinrmv',
|
|
137
|
+
'nucspinkeep',
|
|
138
|
+
'nucspinadd',
|
|
139
|
+
'isotopologues',
|
|
140
|
+
'spinvec',
|
|
141
|
+
'sop',
|
|
142
|
+
'erot',
|
|
143
|
+
# Hamiltonians
|
|
144
|
+
'ham',
|
|
145
|
+
'ham_cf',
|
|
146
|
+
'ham_ez',
|
|
147
|
+
'ham_ezho',
|
|
148
|
+
'ham_hf',
|
|
149
|
+
'ham_ee',
|
|
150
|
+
'ham_nz',
|
|
151
|
+
'ham_nq',
|
|
152
|
+
'ham_nn',
|
|
153
|
+
'ham_oz',
|
|
154
|
+
'ham_so',
|
|
155
|
+
'ham_zf',
|
|
156
|
+
# Spectrum simulation
|
|
157
|
+
'blochsteady',
|
|
158
|
+
'BlochOptions',
|
|
159
|
+
'pepper',
|
|
160
|
+
'garlic',
|
|
161
|
+
'fastmotion',
|
|
162
|
+
'chili',
|
|
163
|
+
'ChiliOptions',
|
|
164
|
+
'endorfrq',
|
|
165
|
+
'salt',
|
|
166
|
+
'levels',
|
|
167
|
+
'resfreqs_matrix',
|
|
168
|
+
'hamsymm',
|
|
169
|
+
'Experiment',
|
|
170
|
+
'Options',
|
|
171
|
+
'sphgrid',
|
|
172
|
+
'sphrand',
|
|
173
|
+
'makespec',
|
|
174
|
+
'convspec',
|
|
175
|
+
# Magnetometry
|
|
176
|
+
'curry',
|
|
177
|
+
# Data I/O (save)
|
|
178
|
+
'eprsave',
|
|
179
|
+
'eprsave_info',
|
|
180
|
+
# Signal processing utilities
|
|
181
|
+
'larmorfrq',
|
|
182
|
+
'equivsplit',
|
|
183
|
+
'equivcouple',
|
|
184
|
+
'datasmooth',
|
|
185
|
+
'rcfilt',
|
|
186
|
+
'hilberttrans',
|
|
187
|
+
'unitconvert',
|
|
188
|
+
'mhz2mt',
|
|
189
|
+
'mt2mhz',
|
|
190
|
+
'hsdim',
|
|
191
|
+
# Nuclear data
|
|
192
|
+
'nucabund',
|
|
193
|
+
'nucspin',
|
|
194
|
+
'nucgval',
|
|
195
|
+
'nucqmom',
|
|
196
|
+
# Lineshape functions
|
|
197
|
+
'gaussian',
|
|
198
|
+
'lorentzian',
|
|
199
|
+
'voigtian',
|
|
200
|
+
'apowin',
|
|
201
|
+
'addnoise',
|
|
202
|
+
'deriv',
|
|
203
|
+
'lshape',
|
|
204
|
+
# Data processing
|
|
205
|
+
'basecorr',
|
|
206
|
+
'fieldmod',
|
|
207
|
+
'rescaledata',
|
|
208
|
+
# Angular momentum functions
|
|
209
|
+
'wigner3j',
|
|
210
|
+
'wigner6j',
|
|
211
|
+
'wignerd',
|
|
212
|
+
'spherharm',
|
|
213
|
+
'clebschgordan',
|
|
214
|
+
'cgmatrix',
|
|
215
|
+
'isto',
|
|
216
|
+
'isto2stev',
|
|
217
|
+
# Perturbation-theory resonance/ENDOR
|
|
218
|
+
'endorfrq_perturb',
|
|
219
|
+
'resfreqs_perturb',
|
|
220
|
+
'resfields_eig',
|
|
221
|
+
'EigOptions',
|
|
222
|
+
# Spin dynamics (spidyan)
|
|
223
|
+
'spidyan',
|
|
224
|
+
'SpidyanOptions',
|
|
225
|
+
'SpidyanInfo',
|
|
226
|
+
# MD trajectory analysis (cardamom Phase 5)
|
|
227
|
+
'mdload',
|
|
228
|
+
'MDData',
|
|
229
|
+
'MDInfo',
|
|
230
|
+
'MDOptions',
|
|
231
|
+
'mdhmm',
|
|
232
|
+
'HMMResult',
|
|
233
|
+
'HMMOptions',
|
|
234
|
+
'mdtraj2oripot',
|
|
235
|
+
# Rotation utilities
|
|
236
|
+
'eulang',
|
|
237
|
+
'euler2quat',
|
|
238
|
+
'quat2euler',
|
|
239
|
+
'quat2rotmat',
|
|
240
|
+
'rotmat2quat',
|
|
241
|
+
'rotaxi2mat',
|
|
242
|
+
'rotmat2axi',
|
|
243
|
+
'quatinv',
|
|
244
|
+
'quatmult',
|
|
245
|
+
'quatvecmult',
|
|
246
|
+
'vec2ang',
|
|
247
|
+
'ang2vec',
|
|
248
|
+
'tensor_cart2sph',
|
|
249
|
+
'tensor_sph2cart',
|
|
250
|
+
'rotateframe',
|
|
251
|
+
# Spin operators
|
|
252
|
+
'commute',
|
|
253
|
+
# Dipolar coupling
|
|
254
|
+
'diptensor',
|
|
255
|
+
# ZFS analysis
|
|
256
|
+
'zfsframes',
|
|
257
|
+
# Spin ladder
|
|
258
|
+
'spinladder',
|
|
259
|
+
'SpinManifold',
|
|
260
|
+
# Orientation selectivity
|
|
261
|
+
'orisel',
|
|
262
|
+
'OriselOptions',
|
|
263
|
+
# Data I/O
|
|
264
|
+
'eprload',
|
|
265
|
+
'eprload_info',
|
|
266
|
+
'orca2torchspin',
|
|
267
|
+
'OrcaData',
|
|
268
|
+
# Pulse EPR
|
|
269
|
+
'saffron',
|
|
270
|
+
'PulseExperiment',
|
|
271
|
+
'SaffronOptions',
|
|
272
|
+
'find_refocusing_pathways',
|
|
273
|
+
# Parameter estimation
|
|
274
|
+
'estimate_parameters',
|
|
275
|
+
'field_from_g',
|
|
276
|
+
'g_from_field',
|
|
277
|
+
'print_parameter_report',
|
|
278
|
+
# Fitting
|
|
279
|
+
'esfit',
|
|
280
|
+
'FitOptions',
|
|
281
|
+
'FitResult',
|
|
282
|
+
# Batch simulation
|
|
283
|
+
'batch_pepper',
|
|
284
|
+
'batch_simulate',
|
|
285
|
+
# Trajectory-based simulation (cardamom)
|
|
286
|
+
'cardamom',
|
|
287
|
+
'CardamomPar',
|
|
288
|
+
'CardamomOptions',
|
|
289
|
+
'MDInput',
|
|
290
|
+
'stochtraj_diffusion',
|
|
291
|
+
'DiffusionPar',
|
|
292
|
+
'stochtraj_jump',
|
|
293
|
+
'JumpPar',
|
|
294
|
+
# Compilation
|
|
295
|
+
'maybe_compile',
|
|
296
|
+
'set_compile_enabled',
|
|
297
|
+
'is_compile_available',
|
|
298
|
+
# Constants
|
|
299
|
+
'BMAGN',
|
|
300
|
+
'PLANCK',
|
|
301
|
+
'GFREE',
|
|
302
|
+
'NMAGN',
|
|
303
|
+
'BOLTZMANN',
|
|
304
|
+
'CLIGHT',
|
|
305
|
+
'EVOLT',
|
|
306
|
+
'HBAR',
|
|
307
|
+
'AMU',
|
|
308
|
+
'ECHARGE',
|
|
309
|
+
'EMASS',
|
|
310
|
+
'PMASS',
|
|
311
|
+
'NMASS',
|
|
312
|
+
'EPS0',
|
|
313
|
+
'MU0',
|
|
314
|
+
'FARADAY',
|
|
315
|
+
'AVOGADRO',
|
|
316
|
+
'BOHRRAD',
|
|
317
|
+
'HARTREE',
|
|
318
|
+
'RYDBERG',
|
|
319
|
+
'GAMMAE',
|
|
320
|
+
'GAMMAN',
|
|
321
|
+
'ANGSTROM',
|
|
322
|
+
'DEGREE',
|
|
323
|
+
'BARN',
|
|
324
|
+
'MOLGAS',
|
|
325
|
+
# EPR conversion
|
|
326
|
+
'eprconvert',
|
|
327
|
+
# Tier 1 utilities
|
|
328
|
+
'sigeq',
|
|
329
|
+
'fdaxis',
|
|
330
|
+
'plegendre',
|
|
331
|
+
'photoselect',
|
|
332
|
+
'levelsplot',
|
|
333
|
+
'LevelsPlotOptions',
|
|
334
|
+
'stackplot',
|
|
335
|
+
# Tier 2 utilities
|
|
336
|
+
'dipkernel',
|
|
337
|
+
'dipbackground',
|
|
338
|
+
'exponfit',
|
|
339
|
+
'nucfrq2d',
|
|
340
|
+
'evolve',
|
|
341
|
+
# Tier 3b — Signal Processing
|
|
342
|
+
'ctafft',
|
|
343
|
+
'ewrls',
|
|
344
|
+
'lpsvd',
|
|
345
|
+
'LPSVDResult',
|
|
346
|
+
'mlpsvd',
|
|
347
|
+
'MLPSVDResult',
|
|
348
|
+
'rapidscan2spc',
|
|
349
|
+
'signalprocessing',
|
|
350
|
+
# Visualization
|
|
351
|
+
'oripotentialplot',
|
|
352
|
+
# Tier 3c — Higher-order Zeeman
|
|
353
|
+
'ham_ezho',
|
|
354
|
+
# Tier 3d-i — Pulse EPR primitives
|
|
355
|
+
'resonatorprofile',
|
|
356
|
+
'transmitter',
|
|
357
|
+
'rfmixer',
|
|
358
|
+
'propint',
|
|
359
|
+
# Tier 3d-ii — Pulse waveforms
|
|
360
|
+
'pulse',
|
|
361
|
+
'exciteprofile',
|
|
362
|
+
'resonator',
|
|
363
|
+
]
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""IST decomposition and Wigner D-matrices for cardamom ISTOs method.
|
|
2
|
+
|
|
3
|
+
Implements:
|
|
4
|
+
- ``istotensor``: irreducible spherical tensor operators from two vector operators
|
|
5
|
+
- ``magint``: IST decomposition of spin Hamiltonian interactions
|
|
6
|
+
- ``wigD``: rank-2 Wigner D-matrices from quaternion trajectories
|
|
7
|
+
|
|
8
|
+
References
|
|
9
|
+
----------
|
|
10
|
+
[1] Oganesyan, Phys. Chem. Chem. Phys. 13, 4724 (2011)
|
|
11
|
+
[2] Mehring, Principles of High Resolution NMR in Solids, Appendix A (1983)
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from torchspin.constants import BMAGN, NMAGN, PLANCK
|
|
17
|
+
from torchspin.spinops import sop
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# istotensor — rank-0, rank-1, rank-2 IST operators from two vector operators
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
def istotensor(
|
|
25
|
+
a: list[np.ndarray] | np.ndarray,
|
|
26
|
+
b: list[np.ndarray] | np.ndarray,
|
|
27
|
+
) -> tuple[np.ndarray, list[np.ndarray], list[np.ndarray]]:
|
|
28
|
+
"""Build irreducible spherical tensor operators from two vector operators.
|
|
29
|
+
|
|
30
|
+
Port of EasySpin's ``private/istotensor.m``.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
a, b:
|
|
35
|
+
Each is either a list/tuple of 3 matrices [ax, ay, az] (spin operators)
|
|
36
|
+
or a length-3 array (e.g. magnetic field components).
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
T0:
|
|
41
|
+
Rank-0 tensor (scalar or matrix).
|
|
42
|
+
T1:
|
|
43
|
+
Rank-1 tensor, list of 3 elements [T1(+1), T1(0), T1(-1)].
|
|
44
|
+
T2:
|
|
45
|
+
Rank-2 tensor, list of 5 elements [T2(+2), T2(+1), T2(0), T2(-1), T2(-2)].
|
|
46
|
+
"""
|
|
47
|
+
if isinstance(a, (list, tuple)):
|
|
48
|
+
ax, ay, az = a[0], a[1], a[2]
|
|
49
|
+
else:
|
|
50
|
+
a = np.asarray(a)
|
|
51
|
+
ax, ay, az = a[0], a[1], a[2]
|
|
52
|
+
|
|
53
|
+
if isinstance(b, (list, tuple)):
|
|
54
|
+
bx, by, bz = b[0], b[1], b[2]
|
|
55
|
+
else:
|
|
56
|
+
b = np.asarray(b)
|
|
57
|
+
bx, by, bz = b[0], b[1], b[2]
|
|
58
|
+
|
|
59
|
+
# Rank 0: T0 = -(1/sqrt(3)) * (ax*bx + ay*by + az*bz)
|
|
60
|
+
T0 = -(1 / np.sqrt(3)) * (ax @ bx + ay @ by + az @ bz) \
|
|
61
|
+
if isinstance(ax, np.ndarray) and ax.ndim == 2 \
|
|
62
|
+
else -(1 / np.sqrt(3)) * (ax * bx + ay * by + az * bz)
|
|
63
|
+
|
|
64
|
+
# Helper for products (handles both matrix and scalar cases)
|
|
65
|
+
def _prod(x, y):
|
|
66
|
+
if isinstance(x, np.ndarray) and x.ndim == 2:
|
|
67
|
+
return x @ y
|
|
68
|
+
return x * y
|
|
69
|
+
|
|
70
|
+
# Rank 1
|
|
71
|
+
T1 = [
|
|
72
|
+
-0.5 * (_prod(ax, bz) - _prod(az, bx) + 1j * (_prod(ay, bz) - _prod(az, by))), # (+1)
|
|
73
|
+
-(1j / np.sqrt(2)) * (_prod(ay, bx) - _prod(ax, by)), # (0)
|
|
74
|
+
-0.5 * (_prod(ax, bz) - _prod(az, bx) - 1j * (_prod(ay, bz) - _prod(az, by))), # (-1)
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
# Rank 2
|
|
78
|
+
T2 = [
|
|
79
|
+
0.5 * (_prod(ax, bx) - _prod(ay, by) + 1j * (_prod(ax, by) + _prod(ay, bx))), # (+2)
|
|
80
|
+
-0.5 * (_prod(ax, bz) + _prod(az, bx) + 1j * (_prod(ay, bz) + _prod(az, by))), # (+1)
|
|
81
|
+
np.sqrt(2 / 3) * (_prod(az, bz) - 0.5 * (_prod(ax, bx) + _prod(ay, by))), # (0)
|
|
82
|
+
0.5 * (_prod(ax, bz) + _prod(az, bx) - 1j * (_prod(ay, bz) + _prod(az, by))), # (-1)
|
|
83
|
+
0.5 * (_prod(ax, bx) - _prod(ay, by) - 1j * (_prod(ax, by) + _prod(ay, bx))), # (-2)
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
return T0, T1, T2
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
# magint — IST decomposition of spin Hamiltonian
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
def magint(
|
|
94
|
+
spins: list[float],
|
|
95
|
+
g: np.ndarray,
|
|
96
|
+
CenterField: float,
|
|
97
|
+
A: np.ndarray | None = None,
|
|
98
|
+
D: np.ndarray | None = None,
|
|
99
|
+
gn: list[float] | None = None,
|
|
100
|
+
nuc_spins: list[float] | None = None,
|
|
101
|
+
include_nuc_zeeman: bool = False,
|
|
102
|
+
) -> tuple[dict, dict]:
|
|
103
|
+
"""Decompose spin Hamiltonian interactions into IST components.
|
|
104
|
+
|
|
105
|
+
Port of EasySpin's ``private/magint.m``.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
spins:
|
|
110
|
+
List of spin quantum numbers [S1, ..., I1, ...].
|
|
111
|
+
g:
|
|
112
|
+
g-tensor, shape (nElectrons, 3) or (3,) for single electron.
|
|
113
|
+
CenterField:
|
|
114
|
+
Center magnetic field in mT.
|
|
115
|
+
A:
|
|
116
|
+
Hyperfine tensor principal values in MHz, shape (nNuclei, 3) or (3,).
|
|
117
|
+
D:
|
|
118
|
+
ZFS tensor principal values in MHz, shape (nElectrons, 3).
|
|
119
|
+
gn:
|
|
120
|
+
Nuclear g-values.
|
|
121
|
+
nuc_spins:
|
|
122
|
+
Nuclear spin quantum numbers.
|
|
123
|
+
include_nuc_zeeman:
|
|
124
|
+
Whether to include nuclear Zeeman interaction.
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
T:
|
|
129
|
+
dict with 'T0' (list of rank-0 ops), 'T2' (list of 5-element lists of rank-2 ops)
|
|
130
|
+
F:
|
|
131
|
+
dict with 'F0' (array of rank-0 spatial), 'F2' (array of rank-2 spatial, shape (nInt, 5))
|
|
132
|
+
"""
|
|
133
|
+
g = np.atleast_2d(np.asarray(g, dtype=float))
|
|
134
|
+
nElSpins = g.shape[0]
|
|
135
|
+
|
|
136
|
+
# Count interactions
|
|
137
|
+
nNuclei = 0
|
|
138
|
+
if nuc_spins is not None:
|
|
139
|
+
nNuclei = len(nuc_spins)
|
|
140
|
+
|
|
141
|
+
# Build spin operators for all spins
|
|
142
|
+
SpinOps = []
|
|
143
|
+
for iSpin in range(len(spins)):
|
|
144
|
+
Sx = sop(spins, [iSpin + 1, 1]).numpy()
|
|
145
|
+
Sy = sop(spins, [iSpin + 1, 2]).numpy()
|
|
146
|
+
Sz = sop(spins, [iSpin + 1, 3]).numpy()
|
|
147
|
+
SpinOps.append([Sx, Sy, Sz])
|
|
148
|
+
|
|
149
|
+
# B0 field vector (z-direction)
|
|
150
|
+
B0 = [0.0, 0.0, CenterField / 1e3] # mT → T
|
|
151
|
+
|
|
152
|
+
T0_list = []
|
|
153
|
+
T2_list = []
|
|
154
|
+
F0_list = []
|
|
155
|
+
F2_list = []
|
|
156
|
+
|
|
157
|
+
# --- Electron Zeeman: muB * B * g * S / h ---
|
|
158
|
+
for iEl in range(nElSpins):
|
|
159
|
+
g_mat = np.diag(g[iEl, :])
|
|
160
|
+
# IST operators: istotensor(B0, S)
|
|
161
|
+
T0, T1, T2 = istotensor(B0, SpinOps[iEl])
|
|
162
|
+
T0_list.append(T0)
|
|
163
|
+
T2_list.append(T2)
|
|
164
|
+
# Spatial components: tensor_cart2sph(g * bmagn / planck) in Hz
|
|
165
|
+
g_scaled = g_mat * (BMAGN / PLANCK) # Hz/T
|
|
166
|
+
F0_scalar = _cart2sph_rank0(g_scaled)
|
|
167
|
+
F2_vec = _tensor_cart2sph_rank2(g_scaled) # 5-element
|
|
168
|
+
F0_list.append(F0_scalar)
|
|
169
|
+
F2_list.append(F2_vec)
|
|
170
|
+
|
|
171
|
+
# --- Hyperfine: S * A * I ---
|
|
172
|
+
if A is not None and nNuclei > 0:
|
|
173
|
+
A = np.atleast_2d(np.asarray(A, dtype=float))
|
|
174
|
+
for iEl in range(nElSpins):
|
|
175
|
+
for iNuc in range(nNuclei):
|
|
176
|
+
A_mat = np.diag(A[iNuc, :]) * 1e6 # MHz → Hz
|
|
177
|
+
S_ops = SpinOps[iEl]
|
|
178
|
+
I_ops = SpinOps[nElSpins + iNuc]
|
|
179
|
+
T0, T1, T2 = istotensor(S_ops, I_ops)
|
|
180
|
+
T0_list.append(T0)
|
|
181
|
+
T2_list.append(T2)
|
|
182
|
+
F0_scalar = _cart2sph_rank0(A_mat)
|
|
183
|
+
F2_vec = _tensor_cart2sph_rank2(A_mat)
|
|
184
|
+
F0_list.append(F0_scalar)
|
|
185
|
+
F2_list.append(F2_vec)
|
|
186
|
+
|
|
187
|
+
# --- ZFS: S * D * S ---
|
|
188
|
+
if D is not None:
|
|
189
|
+
D = np.atleast_2d(np.asarray(D, dtype=float))
|
|
190
|
+
for iEl in range(nElSpins):
|
|
191
|
+
if spins[iEl] < 1:
|
|
192
|
+
continue
|
|
193
|
+
D_mat = np.diag(D[iEl, :]) * 1e6 # MHz → Hz
|
|
194
|
+
T0, T1, T2 = istotensor(SpinOps[iEl], SpinOps[iEl])
|
|
195
|
+
T0_list.append(T0)
|
|
196
|
+
T2_list.append(T2)
|
|
197
|
+
F0_scalar = _cart2sph_rank0(D_mat)
|
|
198
|
+
F2_vec = _tensor_cart2sph_rank2(D_mat)
|
|
199
|
+
F0_list.append(F0_scalar)
|
|
200
|
+
F2_list.append(F2_vec)
|
|
201
|
+
|
|
202
|
+
# --- Nuclear Zeeman ---
|
|
203
|
+
if include_nuc_zeeman and gn is not None:
|
|
204
|
+
for iNuc in range(nNuclei):
|
|
205
|
+
I_ops = SpinOps[nElSpins + iNuc]
|
|
206
|
+
T0, T1, T2 = istotensor(B0, I_ops)
|
|
207
|
+
T0_list.append(T0)
|
|
208
|
+
T2_list.append(T2)
|
|
209
|
+
gn_scaled = -gn[iNuc] * NMAGN / PLANCK # Hz/T (scalar → isotropic tensor)
|
|
210
|
+
F0_list.append(_cart2sph_rank0(gn_scaled * np.eye(3)))
|
|
211
|
+
F2_list.append(_tensor_cart2sph_rank2(gn_scaled * np.eye(3)))
|
|
212
|
+
|
|
213
|
+
F0 = np.array(F0_list, dtype=complex)
|
|
214
|
+
F2 = np.array(F2_list, dtype=complex) if len(F2_list) > 0 else np.zeros((0, 5), dtype=complex)
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
{'T0': T0_list, 'T2': T2_list},
|
|
218
|
+
{'F0': F0, 'F2': F2},
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _tensor_cart2sph_rank2(T: np.ndarray) -> np.ndarray:
|
|
223
|
+
"""Convert 3x3 Cartesian tensor to rank-2 spherical tensor components.
|
|
224
|
+
|
|
225
|
+
Uses the Mehring convention (same as EasySpin's tensor_cart2sph.m):
|
|
226
|
+
T2(0) = sqrt(2/3) * (Tzz - 0.5*(Txx + Tyy))
|
|
227
|
+
|
|
228
|
+
Returns [T2(+2), T2(+1), T2(0), T2(-1), T2(-2)].
|
|
229
|
+
"""
|
|
230
|
+
T = np.asarray(T, dtype=complex)
|
|
231
|
+
Txx, Txy, Txz = T[0, 0], T[0, 1], T[0, 2]
|
|
232
|
+
Tyx, Tyy, Tyz = T[1, 0], T[1, 1], T[1, 2]
|
|
233
|
+
Tzx, Tzy, Tzz = T[2, 0], T[2, 1], T[2, 2]
|
|
234
|
+
|
|
235
|
+
T2p2 = 0.5 * ((Txx - Tyy) + 1j * (Txy + Tyx))
|
|
236
|
+
T2p1 = -0.5 * ((Txz + Tzx) + 1j * (Tyz + Tzy))
|
|
237
|
+
T20 = np.sqrt(2 / 3) * (Tzz - 0.5 * (Txx + Tyy))
|
|
238
|
+
T2m1 = 0.5 * ((Txz + Tzx) - 1j * (Tyz + Tzy))
|
|
239
|
+
T2m2 = 0.5 * ((Txx - Tyy) - 1j * (Txy + Tyx))
|
|
240
|
+
|
|
241
|
+
return np.array([T2p2, T2p1, T20, T2m1, T2m2])
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _cart2sph_rank0(T: np.ndarray) -> complex:
|
|
245
|
+
"""Extract rank-0 (isotropic) component from a 3x3 tensor.
|
|
246
|
+
|
|
247
|
+
F0 = -(1/sqrt(3)) * Tr(T)
|
|
248
|
+
"""
|
|
249
|
+
T = np.asarray(T, dtype=complex)
|
|
250
|
+
return -(1 / np.sqrt(3)) * np.trace(T)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# ---------------------------------------------------------------------------
|
|
254
|
+
# wigD — rank-2 Wigner D-matrices from quaternion trajectories
|
|
255
|
+
# ---------------------------------------------------------------------------
|
|
256
|
+
|
|
257
|
+
def wigD(q: np.ndarray) -> np.ndarray:
|
|
258
|
+
"""Compute rank-2 Wigner D-matrices from quaternion trajectories.
|
|
259
|
+
|
|
260
|
+
Port of the ``wigD`` subfunction in EasySpin's ``cardamom_propagatedm.m``.
|
|
261
|
+
|
|
262
|
+
Parameters
|
|
263
|
+
----------
|
|
264
|
+
q:
|
|
265
|
+
Quaternion trajectory, shape ``(4, nSteps, nTraj)`` or ``(4, nSteps)``.
|
|
266
|
+
Convention: q = [q0, q1, q2, q3] where q0 is scalar part.
|
|
267
|
+
|
|
268
|
+
Returns
|
|
269
|
+
-------
|
|
270
|
+
D2:
|
|
271
|
+
Rank-2 Wigner D-matrix trajectory, shape ``(5, 5, nSteps, nTraj)``.
|
|
272
|
+
Indices correspond to m' = +2, +1, 0, -1, -2 (rows)
|
|
273
|
+
and m = +2, +1, 0, -1, -2 (columns).
|
|
274
|
+
"""
|
|
275
|
+
if q.ndim == 2:
|
|
276
|
+
q = q[:, :, np.newaxis]
|
|
277
|
+
|
|
278
|
+
# Quaternion components → Cayley-Klein parameters
|
|
279
|
+
A = q[0] - 1j * q[3]
|
|
280
|
+
B = -q[2] - 1j * q[1]
|
|
281
|
+
Ast = q[0] + 1j * q[3]
|
|
282
|
+
Bst = -q[2] + 1j * q[1]
|
|
283
|
+
|
|
284
|
+
Z = A * Ast - B * Bst
|
|
285
|
+
|
|
286
|
+
ASq = A * A
|
|
287
|
+
BSq = B * B
|
|
288
|
+
AstSq = Ast * Ast
|
|
289
|
+
BstSq = Bst * Bst
|
|
290
|
+
|
|
291
|
+
nSteps = q.shape[1]
|
|
292
|
+
nTraj = q.shape[2]
|
|
293
|
+
D2 = np.zeros((5, 5, nSteps, nTraj), dtype=complex)
|
|
294
|
+
|
|
295
|
+
sqrt6 = np.sqrt(6)
|
|
296
|
+
|
|
297
|
+
D2[0, 0] = ASq * ASq
|
|
298
|
+
D2[0, 1] = 2 * A**3 * B
|
|
299
|
+
D2[0, 2] = sqrt6 * ASq * BSq
|
|
300
|
+
D2[0, 3] = 2 * A * B**3
|
|
301
|
+
D2[0, 4] = BSq * BSq
|
|
302
|
+
|
|
303
|
+
D2[1, 0] = -2 * A**3 * Bst
|
|
304
|
+
D2[1, 1] = ASq * (2 * Z - 1)
|
|
305
|
+
D2[1, 2] = sqrt6 * A * B * Z
|
|
306
|
+
D2[1, 3] = BSq * (2 * Z + 1)
|
|
307
|
+
D2[1, 4] = 2 * Ast * B**3
|
|
308
|
+
|
|
309
|
+
D2[2, 0] = sqrt6 * ASq * BstSq
|
|
310
|
+
D2[2, 1] = -sqrt6 * A * Bst * Z
|
|
311
|
+
D2[2, 2] = 0.5 * (3 * Z**2 - 1)
|
|
312
|
+
D2[2, 3] = sqrt6 * Ast * B * Z
|
|
313
|
+
D2[2, 4] = sqrt6 * AstSq * BSq
|
|
314
|
+
|
|
315
|
+
D2[3, 0] = -2 * A * Bst**3
|
|
316
|
+
D2[3, 1] = BstSq * (2 * Z + 1)
|
|
317
|
+
D2[3, 2] = -sqrt6 * Ast * Bst * Z
|
|
318
|
+
D2[3, 3] = AstSq * (2 * Z - 1)
|
|
319
|
+
D2[3, 4] = 2 * Ast**3 * B
|
|
320
|
+
|
|
321
|
+
D2[4, 0] = BstSq * BstSq
|
|
322
|
+
D2[4, 1] = -2 * Ast * Bst**3
|
|
323
|
+
D2[4, 2] = sqrt6 * AstSq * BstSq
|
|
324
|
+
D2[4, 3] = -2 * Ast**3 * Bst
|
|
325
|
+
D2[4, 4] = AstSq * AstSq
|
|
326
|
+
|
|
327
|
+
return D2
|