qdmag 1.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.
- qdmag/__init__.py +7 -0
- qdmag/core/Operators.py +491 -0
- qdmag/core/StevensOperators.py +481 -0
- qdmag/core/__init__.py +0 -0
- qdmag/core/analysis.py +73 -0
- qdmag/core/backend.py +76 -0
- qdmag/core/classical_rate.py +337 -0
- qdmag/core/common.py +1061 -0
- qdmag/core/constants.py +8 -0
- qdmag/core/effective_basis.py +381 -0
- qdmag/core/expm.py +128 -0
- qdmag/core/hdf5.py +109 -0
- qdmag/core/liouville.py +1618 -0
- qdmag/core/powder_dynamics.py +235 -0
- qdmag/core/pulse.py +101 -0
- qdmag/core/quantum_master.py +153 -0
- qdmag/tools/__init__.py +0 -0
- qdmag/tools/tool_RK4.py +31 -0
- qdmag/tools/tool_basis.py +83 -0
- qdmag/tools/tool_benchmark.py +259 -0
- qdmag/tools/tool_commutation.py +49 -0
- qdmag/tools/tool_conditions_rho.py +33 -0
- qdmag/tools/tool_divergence.py +33 -0
- qdmag/tools/tool_eigen.py +30 -0
- qdmag/tools/tool_hdf5.py +18 -0
- qdmag/tools/tool_magnetization.py +35 -0
- qdmag/tools/tool_occupation.py +29 -0
- qdmag/tools/tool_powder.py +522 -0
- qdmag/tools/tool_powder_dynamics.py +51 -0
- qdmag/tools/tool_projection.py +23 -0
- qdmag/tools/tool_quadrature.py +192 -0
- qdmag/tools/tool_rate.py +65 -0
- qdmag/tools/tool_spins.py +28 -0
- qdmag/tools/tool_staircase.py +31 -0
- qdmag/tools/tool_time_step.py +382 -0
- qdmag/tools/tool_transform_rho.py +47 -0
- qdmag/tools/tool_zeeman.py +35 -0
- qdmag-1.3.0.dist-info/METADATA +136 -0
- qdmag-1.3.0.dist-info/RECORD +42 -0
- qdmag-1.3.0.dist-info/WHEEL +5 -0
- qdmag-1.3.0.dist-info/licenses/LICENSE +674 -0
- qdmag-1.3.0.dist-info/top_level.txt +1 -0
qdmag/__init__.py
ADDED
qdmag/core/Operators.py
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
# The source code is modified from the original code in the PyCrystalField package by Allen Scheie.
|
|
2
|
+
# Here is the link to the original code: https://github.com/asche1/PyCrystalField/tree/master
|
|
3
|
+
# The original code is licensed under the GPL-3.0 license
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
class Ket():
|
|
8
|
+
def __init__(self, array):
|
|
9
|
+
"""give an array which defines the angular momentum eigenket in terms
|
|
10
|
+
of the available states. IE, write |1> + |2> as [0,0,0,1,1]"""
|
|
11
|
+
self.ket = np.array(array)
|
|
12
|
+
self.j = len(array)/2.0 - 0.5
|
|
13
|
+
self.m = np.arange(-self.j,self.j+1,1)
|
|
14
|
+
|
|
15
|
+
def Jz(self):
|
|
16
|
+
return Ket( self.ket * self.m )
|
|
17
|
+
|
|
18
|
+
def Jplus(self):
|
|
19
|
+
newvals = np.sqrt((self.j-self.m)*(self.j+self.m+1)) * self.ket
|
|
20
|
+
return Ket( np.roll(newvals,1) )
|
|
21
|
+
|
|
22
|
+
def Jminus(self):
|
|
23
|
+
newvals = np.sqrt((self.j+self.m)*(self.j-self.m+1)) * self.ket
|
|
24
|
+
return Ket( np.roll(newvals,-1) )
|
|
25
|
+
|
|
26
|
+
def Jx(self):
|
|
27
|
+
return Ket(0.5*(self.Jplus().ket + self.Jminus().ket) )
|
|
28
|
+
|
|
29
|
+
def Jy(self):
|
|
30
|
+
return Ket(-1j*0.5*(self.Jplus().ket - self.Jminus().ket) )
|
|
31
|
+
|
|
32
|
+
def R(self, alpha, beta, gamma): # Rotation about general Euler Angles
|
|
33
|
+
return self._Rz(alpha)._Ry(beta)._Rz(gamma)
|
|
34
|
+
|
|
35
|
+
def _Rz(self,theta): # Rotation about z axis
|
|
36
|
+
newvals = np.zeros(len(self.ket), dtype=complex)
|
|
37
|
+
for i in range(len(self.ket)):
|
|
38
|
+
newvals[i] = self.ket[i]* np.exp(-1j*self.m[i] * theta)
|
|
39
|
+
return Ket(newvals)
|
|
40
|
+
|
|
41
|
+
def _Ry(self,beta): # Rotation about y axis
|
|
42
|
+
newvals = np.zeros(len(self.ket), dtype=complex)
|
|
43
|
+
for i in range(len(self.ket)):
|
|
44
|
+
mm = self.m[i]
|
|
45
|
+
for j in range(len(self.ket)):
|
|
46
|
+
mmp = self.m[j]
|
|
47
|
+
newvals[j] += self.ket[i]* self._WignersFormula(mm,mmp,beta)
|
|
48
|
+
return Ket(newvals)
|
|
49
|
+
|
|
50
|
+
def _WignersFormula(self,m,mp,beta):
|
|
51
|
+
"""See Sakurai/Napolitano eq. 3.9.33.
|
|
52
|
+
This function was cross-checked with Mathematica's WignerD function."""
|
|
53
|
+
|
|
54
|
+
# determine the limit of the sum over k
|
|
55
|
+
kmin = np.maximum(0, m-mp)
|
|
56
|
+
kmax = np.minimum(self.j+m, self.j-mp)
|
|
57
|
+
|
|
58
|
+
d = 0
|
|
59
|
+
for k in np.arange(kmin,kmax+1):
|
|
60
|
+
d += (-1)**(k-m+mp) * np.sqrt(np.math.factorial(self.j+m) * np.math.factorial(self.j-m) *\
|
|
61
|
+
np.math.factorial(self.j+mp) * np.math.factorial(self.j-mp))/\
|
|
62
|
+
(np.math.factorial(self.j+m-k) * np.math.factorial(k) * np.math.factorial(self.j-k-mp)*\
|
|
63
|
+
np.math.factorial(k-m+mp))*\
|
|
64
|
+
np.cos(beta/2)**(2*self.j -2*k+m-mp) * np.sin(beta/2)**(2*k-m+mp)
|
|
65
|
+
return d
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def __mul__(self,other):
|
|
69
|
+
if isinstance(other, Ket):
|
|
70
|
+
# Compute inner product
|
|
71
|
+
return np.dot(np.conjugate(self.ket), other.ket)
|
|
72
|
+
else:
|
|
73
|
+
return Ket( np.dot(self.ket, other))
|
|
74
|
+
|
|
75
|
+
def __add__(self,other):
|
|
76
|
+
if isinstance(other, Ket):
|
|
77
|
+
return Ket(self.ket + other.ket)
|
|
78
|
+
else:
|
|
79
|
+
print("other is not a ket")
|
|
80
|
+
|
|
81
|
+
# def __rmul__(self,other): #Doesn't work. not sure why.
|
|
82
|
+
# if isinstance(other, Ket):
|
|
83
|
+
# return np.vdot(other.ket, self.ket)
|
|
84
|
+
# else: # if not ket, try matrix multiplication.
|
|
85
|
+
# #print "reverse multiply"
|
|
86
|
+
# return np.dot(other, self.ket)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# spec = [
|
|
91
|
+
# ('O', float32[:,:]), # a simple scalar field
|
|
92
|
+
# #('j', float32),
|
|
93
|
+
# ('m', float32[:]), # an array field
|
|
94
|
+
# ]
|
|
95
|
+
|
|
96
|
+
# @jitclass(spec) # Doesn't work
|
|
97
|
+
class Operator():
|
|
98
|
+
def __init__(self, J):
|
|
99
|
+
self.O = np.zeros((int(2*J+1), int(2*J+1)))
|
|
100
|
+
self.m = np.arange(-J,J+1,1)
|
|
101
|
+
self.j = J
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def Jz(J):
|
|
105
|
+
obj = Operator(J)
|
|
106
|
+
for i in range(len(obj.O)):
|
|
107
|
+
for k in range(len(obj.O)):
|
|
108
|
+
if i == k:
|
|
109
|
+
obj.O[i,k] = (obj.m[k])
|
|
110
|
+
return obj
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def Jplus(J):
|
|
114
|
+
obj = Operator(J)
|
|
115
|
+
for i in range(len(obj.O)):
|
|
116
|
+
for k in range(len(obj.O)):
|
|
117
|
+
if k+1 == i:
|
|
118
|
+
obj.O[i,k] = np.sqrt((obj.j-obj.m[k])*(obj.j+obj.m[k]+1))
|
|
119
|
+
return obj
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
def Jminus(J):
|
|
123
|
+
obj = Operator(J)
|
|
124
|
+
for i in range(len(obj.O)):
|
|
125
|
+
for k in range(len(obj.O)):
|
|
126
|
+
if k-1 == i:
|
|
127
|
+
obj.O[i,k] = np.sqrt((obj.j+obj.m[k])*(obj.j-obj.m[k]+1))
|
|
128
|
+
return obj
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def Jx(J):
|
|
132
|
+
objp = Operator.Jplus(J)
|
|
133
|
+
objm = Operator.Jminus(J)
|
|
134
|
+
return 0.5*objp + 0.5*objm
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def Jy(J):
|
|
138
|
+
objp = Operator.Jplus(J)
|
|
139
|
+
objm = Operator.Jminus(J)
|
|
140
|
+
return -0.5j*objp + 0.5j*objm
|
|
141
|
+
|
|
142
|
+
def __add__(self,other):
|
|
143
|
+
newobj = Operator(self.j)
|
|
144
|
+
if isinstance(other, Operator):
|
|
145
|
+
newobj.O = self.O + other.O
|
|
146
|
+
else:
|
|
147
|
+
newobj.O = self.O + other*np.identity(int(2*self.j+1))
|
|
148
|
+
return newobj
|
|
149
|
+
|
|
150
|
+
def __radd__(self,other):
|
|
151
|
+
newobj = Operator(self.j)
|
|
152
|
+
if isinstance(other, Operator):
|
|
153
|
+
newobj.O = self.O + other.O
|
|
154
|
+
else:
|
|
155
|
+
newobj.O = self.O + other*np.identity(int(2*self.j+1))
|
|
156
|
+
return newobj
|
|
157
|
+
|
|
158
|
+
def __sub__(self,other):
|
|
159
|
+
newobj = Operator(self.j)
|
|
160
|
+
if isinstance(other, Operator):
|
|
161
|
+
newobj.O = self.O - other.O
|
|
162
|
+
else:
|
|
163
|
+
newobj.O = self.O - other*np.identity(int(2*self.j+1))
|
|
164
|
+
return newobj
|
|
165
|
+
|
|
166
|
+
def __mul__(self,other):
|
|
167
|
+
newobj = Operator(self.j)
|
|
168
|
+
if (isinstance(other, int) or isinstance(other, float) or isinstance(other, complex)):
|
|
169
|
+
newobj.O = other * self.O
|
|
170
|
+
else:
|
|
171
|
+
newobj.O = np.dot(self.O, other.O)
|
|
172
|
+
return newobj
|
|
173
|
+
|
|
174
|
+
def __rmul__(self,other):
|
|
175
|
+
newobj = Operator(self.j)
|
|
176
|
+
if (isinstance(other, int) or isinstance(other, float) or isinstance(other, complex)):
|
|
177
|
+
newobj.O = other * self.O
|
|
178
|
+
else:
|
|
179
|
+
newobj.O = np.dot(other.O, self.O)
|
|
180
|
+
return newobj
|
|
181
|
+
|
|
182
|
+
def __pow__(self, power):
|
|
183
|
+
newobj = Operator(self.j)
|
|
184
|
+
newobj.O = self.O
|
|
185
|
+
for i in range(power-1):
|
|
186
|
+
newobj.O = np.dot(newobj.O,self.O)
|
|
187
|
+
return newobj
|
|
188
|
+
|
|
189
|
+
def __neg__(self):
|
|
190
|
+
newobj = Operator(self.j)
|
|
191
|
+
newobj.O = -self.O
|
|
192
|
+
return newobj
|
|
193
|
+
|
|
194
|
+
def __repr__(self):
|
|
195
|
+
return repr(self.O)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
####################################3
|
|
204
|
+
##
|
|
205
|
+
## ## #######
|
|
206
|
+
## ## #############
|
|
207
|
+
## ## ### ###
|
|
208
|
+
## ## ###
|
|
209
|
+
## ## ####
|
|
210
|
+
## ## #####
|
|
211
|
+
## ## #####
|
|
212
|
+
## ## ####
|
|
213
|
+
## ## ###
|
|
214
|
+
## ## ### ###
|
|
215
|
+
## ########### #### ####
|
|
216
|
+
## ########### #######
|
|
217
|
+
##
|
|
218
|
+
######################################
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class LSOperator():
|
|
226
|
+
'''This is for a full treatment in the intermediate coupling scheme'''
|
|
227
|
+
def __init__(self, L, S):
|
|
228
|
+
self.O = np.zeros((int((2*L+1)*(2*S+1)), int((2*L+1)*(2*S+1)) ))
|
|
229
|
+
self.L = L
|
|
230
|
+
self.S = S
|
|
231
|
+
lm = np.arange(-L,L+1,1)
|
|
232
|
+
sm = np.arange(-S,S+1,1)
|
|
233
|
+
self.Lm = np.repeat(lm, len(sm))
|
|
234
|
+
self.Sm = np.tile(sm, len(lm))
|
|
235
|
+
|
|
236
|
+
@staticmethod
|
|
237
|
+
def Lz(L, S):
|
|
238
|
+
obj = LSOperator(L, S)
|
|
239
|
+
for i in range(len(obj.O)):
|
|
240
|
+
for k in range(len(obj.O)):
|
|
241
|
+
if i == k:
|
|
242
|
+
obj.O[i,k] = (obj.Lm[k])
|
|
243
|
+
return obj
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
def Lplus(L, S):
|
|
247
|
+
obj = LSOperator(L, S)
|
|
248
|
+
for i, lm1 in enumerate(obj.Lm):
|
|
249
|
+
for k, lm2 in enumerate(obj.Lm):
|
|
250
|
+
if (lm1 - lm2 == 1) and (obj.Sm[i] == obj.Sm[k] ):
|
|
251
|
+
obj.O[i,k] = np.sqrt((obj.L-obj.Lm[k])*(obj.L+obj.Lm[k]+1))
|
|
252
|
+
return obj
|
|
253
|
+
|
|
254
|
+
@staticmethod
|
|
255
|
+
def Lminus(L, S):
|
|
256
|
+
obj = LSOperator(L, S)
|
|
257
|
+
for i, lm1 in enumerate(obj.Lm):
|
|
258
|
+
for k, lm2 in enumerate(obj.Lm):
|
|
259
|
+
if (lm2 - lm1 == 1) and (obj.Sm[i] == obj.Sm[k]):
|
|
260
|
+
obj.O[i,k] = np.sqrt((obj.L+obj.Lm[k])*(obj.L-obj.Lm[k]+1))
|
|
261
|
+
return obj
|
|
262
|
+
|
|
263
|
+
@staticmethod
|
|
264
|
+
def Lx(L, S):
|
|
265
|
+
objp = LSOperator.Lplus(L, S)
|
|
266
|
+
objm = LSOperator.Lminus(L, S)
|
|
267
|
+
return 0.5*objp + 0.5*objm
|
|
268
|
+
|
|
269
|
+
@staticmethod
|
|
270
|
+
def Ly(L, S):
|
|
271
|
+
objp = LSOperator.Lplus(L, S)
|
|
272
|
+
objm = LSOperator.Lminus(L, S)
|
|
273
|
+
return -0.5j*objp + 0.5j*objm
|
|
274
|
+
|
|
275
|
+
##################################
|
|
276
|
+
# Spin operators
|
|
277
|
+
@staticmethod
|
|
278
|
+
def Sz(L, S):
|
|
279
|
+
obj = LSOperator(L, S)
|
|
280
|
+
for i in range(len(obj.O)):
|
|
281
|
+
for k in range(len(obj.O)):
|
|
282
|
+
if i == k:
|
|
283
|
+
obj.O[i,k] = (obj.Sm[k])
|
|
284
|
+
return obj
|
|
285
|
+
|
|
286
|
+
@staticmethod
|
|
287
|
+
def Splus(L, S):
|
|
288
|
+
obj = LSOperator(L, S)
|
|
289
|
+
for i, sm1 in enumerate(obj.Sm):
|
|
290
|
+
for k, sm2 in enumerate(obj.Sm):
|
|
291
|
+
if (sm1 - sm2 == 1) and (obj.Lm[i] == obj.Lm[k]):
|
|
292
|
+
obj.O[i,k] = np.sqrt((obj.S-obj.Sm[k])*(obj.S+obj.Sm[k]+1))
|
|
293
|
+
return obj
|
|
294
|
+
|
|
295
|
+
@staticmethod
|
|
296
|
+
def Sminus(L, S):
|
|
297
|
+
obj = LSOperator(L, S)
|
|
298
|
+
for i, sm1 in enumerate(obj.Sm):
|
|
299
|
+
for k, sm2 in enumerate(obj.Sm):
|
|
300
|
+
if (sm2 - sm1 == 1) and (obj.Lm[i] == obj.Lm[k]):
|
|
301
|
+
obj.O[i,k] = np.sqrt((obj.S+obj.Sm[k])*(obj.S-obj.Sm[k]+1))
|
|
302
|
+
return obj
|
|
303
|
+
|
|
304
|
+
@staticmethod
|
|
305
|
+
def Sx(L, S):
|
|
306
|
+
objp = LSOperator.Splus(L, S)
|
|
307
|
+
objm = LSOperator.Sminus(L, S)
|
|
308
|
+
return 0.5*objp + 0.5*objm
|
|
309
|
+
|
|
310
|
+
@staticmethod
|
|
311
|
+
def Sy(L, S):
|
|
312
|
+
objp = LSOperator.Splus(L, S)
|
|
313
|
+
objm = LSOperator.Sminus(L, S)
|
|
314
|
+
return -0.5j*objp + 0.5j*objm
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def __add__(self,other):
|
|
318
|
+
newobj = LSOperator(self.L, self.S)
|
|
319
|
+
try:
|
|
320
|
+
newobj.O = np.add(self.O, other.O)
|
|
321
|
+
except AttributeError:
|
|
322
|
+
newobj.O = self.O + other*np.identity(len(self.O))
|
|
323
|
+
return newobj
|
|
324
|
+
|
|
325
|
+
def __radd__(self,other):
|
|
326
|
+
newobj = LSOperator(self.L, self.S)
|
|
327
|
+
try:
|
|
328
|
+
newobj.O = np.add(other.O, self.O)
|
|
329
|
+
except AttributeError:
|
|
330
|
+
newobj.O = self.O + other*np.identity(len(self.O))
|
|
331
|
+
return newobj
|
|
332
|
+
|
|
333
|
+
def __sub__(self,other):
|
|
334
|
+
newobj = LSOperator(self.L, self.S)
|
|
335
|
+
try:
|
|
336
|
+
newobj.O = self.O - other.O
|
|
337
|
+
except AttributeError:
|
|
338
|
+
newobj.O = self.O - other*np.identity(len(self.O))
|
|
339
|
+
return newobj
|
|
340
|
+
|
|
341
|
+
def __mul__(self,other):
|
|
342
|
+
newobj = LSOperator(self.L, self.S)
|
|
343
|
+
try:
|
|
344
|
+
newobj.O = np.dot(self.O, other.O)
|
|
345
|
+
except AttributeError:
|
|
346
|
+
newobj.O = other * self.O
|
|
347
|
+
return newobj
|
|
348
|
+
|
|
349
|
+
def __rmul__(self,other):
|
|
350
|
+
newobj = LSOperator(self.L, self.S)
|
|
351
|
+
try:
|
|
352
|
+
newobj.O = np.dot(other.O, self.O)
|
|
353
|
+
except AttributeError:
|
|
354
|
+
newobj.O = other * self.O
|
|
355
|
+
return newobj
|
|
356
|
+
|
|
357
|
+
def __pow__(self, power):
|
|
358
|
+
newobj = LSOperator(self.L, self.S)
|
|
359
|
+
newobj.O = self.O
|
|
360
|
+
for i in range(power-1):
|
|
361
|
+
newobj.O = np.dot(newobj.O,self.O)
|
|
362
|
+
return newobj
|
|
363
|
+
|
|
364
|
+
def __neg__(self):
|
|
365
|
+
newobj = LSOperator(self.L, self.S)
|
|
366
|
+
newobj.O = -self.O
|
|
367
|
+
return newobj
|
|
368
|
+
|
|
369
|
+
def __repr__(self):
|
|
370
|
+
return repr(self.O)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
# Computing magnetization and susceptibility
|
|
374
|
+
|
|
375
|
+
def magnetization(self, Temp, Field):
|
|
376
|
+
'''field should be a 3-component vector. Temps may be an array.'''
|
|
377
|
+
if len(Field) != 3:
|
|
378
|
+
raise TypeError("Field needs to be 3-component vector")
|
|
379
|
+
|
|
380
|
+
# A) Define magnetic Hamiltonian
|
|
381
|
+
Lx = LSOperator.Lx(self.L, self.S)
|
|
382
|
+
Ly = LSOperator.Ly(self.L, self.S)
|
|
383
|
+
Lz = LSOperator.Lz(self.L, self.S)
|
|
384
|
+
Sx = LSOperator.Sx(self.L, self.S)
|
|
385
|
+
Sy = LSOperator.Sy(self.L, self.S)
|
|
386
|
+
Sz = LSOperator.Sz(self.L, self.S)
|
|
387
|
+
|
|
388
|
+
g0 = 2.002319
|
|
389
|
+
Jx = Lx + g0*Sx
|
|
390
|
+
Jy = Ly + g0*Sy
|
|
391
|
+
Jz = Lz + g0*Sz
|
|
392
|
+
|
|
393
|
+
muB = 5.7883818012e-2 # meV/T
|
|
394
|
+
#mu0 = np.pi*4e-7 # T*m/A
|
|
395
|
+
JdotB = muB*((Field[0]*Lx + Field[1]*Ly + Field[2]*Lz) +\
|
|
396
|
+
(Field[0]*Sx + Field[1]*Sy + Field[2]*Sz))
|
|
397
|
+
|
|
398
|
+
# B) Diagonalize full Hamiltonian
|
|
399
|
+
FieldHam = self.O + JdotB.O
|
|
400
|
+
diagonalH = LA.eigh(FieldHam)
|
|
401
|
+
|
|
402
|
+
minE = np.amin(diagonalH[0])
|
|
403
|
+
evals = diagonalH[0] - minE
|
|
404
|
+
evecs = diagonalH[1].T
|
|
405
|
+
# These ARE actual eigenvalues.
|
|
406
|
+
|
|
407
|
+
# C) Compute expectation value along field
|
|
408
|
+
JexpVals = np.zeros((len(evals),3))
|
|
409
|
+
for i, ev in enumerate(evecs):
|
|
410
|
+
#print np.real(np.dot(ev, np.dot( self.O ,ev))), diagonalH[0][i]
|
|
411
|
+
#print np.real(np.dot( FieldHam ,ev)), np.real(diagonalH[0][i]*ev)
|
|
412
|
+
JexpVals[i] =[np.real(np.dot(np.conjugate(ev), np.dot( Jx.O ,ev))),
|
|
413
|
+
np.real(np.dot(np.conjugate(ev), np.dot( Jy.O ,ev))),
|
|
414
|
+
np.real(np.dot(np.conjugate(ev), np.dot( Jz.O ,ev)))]
|
|
415
|
+
k_B = 8.6173303e-2 # meV/K
|
|
416
|
+
|
|
417
|
+
if (isinstance(Temp, int) or isinstance(Temp, float)):
|
|
418
|
+
Zz = np.sum(np.exp(-evals/(k_B*Temp)))
|
|
419
|
+
JexpVal = np.dot(np.exp(-evals/(k_B*Temp)),JexpVals)/Zz
|
|
420
|
+
return np.real(JexpVal)
|
|
421
|
+
else:
|
|
422
|
+
expvals, temps = np.meshgrid(evals, Temp)
|
|
423
|
+
ZZ = np.sum(np.exp(-expvals/temps/k_B), axis=1)
|
|
424
|
+
JexpValList = np.repeat(JexpVals.reshape((1,)+JexpVals.shape), len(Temp), axis=0)
|
|
425
|
+
JexpValList = np.sum(np.exp(-expvals/temps/k_B)*\
|
|
426
|
+
np.transpose(JexpValList, axes=[2,0,1]), axis=2) / ZZ
|
|
427
|
+
# if np.isnan(JexpValList).any():
|
|
428
|
+
# print -expvals[0]/temps[0]/k_B
|
|
429
|
+
# print np.exp(-expvals/temps/k_B)[0]
|
|
430
|
+
# raise ValueError('Nan in result!')
|
|
431
|
+
return np.nan_to_num(JexpValList.T)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def susceptibility(self, Temps, Field, deltaField):
|
|
435
|
+
'''Computes susceptibility numerically with a numerical derivative.
|
|
436
|
+
deltaField needs to be a scalar value.'''
|
|
437
|
+
if not isinstance(deltaField, float):
|
|
438
|
+
raise TypeError("Deltafield needs to be a scalar")
|
|
439
|
+
|
|
440
|
+
if isinstance(Field, float):
|
|
441
|
+
# Assume we are computing a powder average
|
|
442
|
+
VecField = Field * np.array([1,0,0])
|
|
443
|
+
Delta = deltaField*np.array(VecField)/Field
|
|
444
|
+
Mplus1 = self.magnetization(Temps, VecField + Delta)
|
|
445
|
+
Mminus1= self.magnetization(Temps, VecField - Delta)
|
|
446
|
+
Mplus2 = self.magnetization(Temps, VecField + 2*Delta)
|
|
447
|
+
Mminus2= self.magnetization(Temps, VecField - 2*Delta)
|
|
448
|
+
|
|
449
|
+
dMdH_x = (8*(Mplus1 - Mminus1) - (Mplus2 - Mminus2))/(12*deltaField)
|
|
450
|
+
|
|
451
|
+
VecField = Field * np.array([0,1,0])
|
|
452
|
+
Delta = deltaField*np.array(VecField)/Field
|
|
453
|
+
Mplus1 = self.magnetization(Temps, VecField + Delta)
|
|
454
|
+
Mminus1= self.magnetization(Temps, VecField - Delta)
|
|
455
|
+
Mplus2 = self.magnetization(Temps, VecField + 2*Delta)
|
|
456
|
+
Mminus2= self.magnetization(Temps, VecField - 2*Delta)
|
|
457
|
+
|
|
458
|
+
dMdH_y = (8*(Mplus1 - Mminus1) - (Mplus2 - Mminus2))/(12*deltaField)
|
|
459
|
+
|
|
460
|
+
VecField = Field * np.array([0,0,1])
|
|
461
|
+
Delta = deltaField*np.array(VecField)/Field
|
|
462
|
+
Mplus1 = self.magnetization(Temps, VecField + Delta)
|
|
463
|
+
Mminus1= self.magnetization(Temps, VecField - Delta)
|
|
464
|
+
Mplus2 = self.magnetization(Temps, VecField + 2*Delta)
|
|
465
|
+
Mminus2= self.magnetization(Temps, VecField - 2*Delta)
|
|
466
|
+
|
|
467
|
+
dMdH_z = (8*(Mplus1 - Mminus1) - (Mplus2 - Mminus2))/(12*deltaField)
|
|
468
|
+
|
|
469
|
+
return (dMdH_x[:,0]+dMdH_y[:,1]+dMdH_z[:,2])/3.
|
|
470
|
+
|
|
471
|
+
elif len(Field) == 3:
|
|
472
|
+
Delta = deltaField*np.array(Field)/np.linalg.norm(Field)
|
|
473
|
+
Mplus1 = self.magnetization(Temps, Field + Delta)
|
|
474
|
+
Mminus1= self.magnetization(Temps, Field - Delta)
|
|
475
|
+
Mplus2 = self.magnetization(Temps, Field + 2*Delta)
|
|
476
|
+
Mminus2= self.magnetization(Temps, Field - 2*Delta)
|
|
477
|
+
|
|
478
|
+
dMdH = (8*(Mplus1 - Mminus1) - (Mplus2 - Mminus2))/(12*deltaField)
|
|
479
|
+
#dMdH = (Mplus1 - Mminus1)/(2*deltaField)
|
|
480
|
+
|
|
481
|
+
return dMdH
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
|