(git:6344619)
Loading...
Searching...
No Matches
kubo_unittest.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
8 IMPLICIT NONE
9 PRIVATE
11CONTAINS
12
13! **************************************************************************************************
14!> \brief Validate projected currents and their AO-metric covariance.
15! **************************************************************************************************
17 USE kinds, ONLY: dp
19 COMPLEX(dp) :: b(4, 4), h(4, 4), h2(4, 4), j(4, 4, 3), j2(4, 4, 3), norm(3, 3), norm2(3, 3), &
20 s(4, 4), s2(4, 4), x(4, 4, 3), x2(4, 4, 3)
21 INTEGER :: a, c, i, status
22 REAL(dp) :: e(4), e2(4)
23
24 h = 0.0_dp
25 s = 0.0_dp
26 x = 0.0_dp
27 DO i = 1, 4
28 s(i, i) = 1.0_dp
29 END DO
30 ! Kramers-degenerate spinor model with imaginary SOC matrix elements.
31 h(2, 2) = 0.8_dp
32 h(4, 4) = 0.8_dp
33 h(1, 2) = cmplx(0.0_dp, 0.3_dp, dp)
34 h(2, 1) = conjg(h(1, 2))
35 h(3, 4) = conjg(h(1, 2))
36 h(4, 3) = h(1, 2)
37 x(1, 2, 1) = 1.0_dp
38 x(2, 1, 1) = 1.0_dp
39 x(3, 4, 1) = 1.0_dp
40 x(4, 3, 1) = 1.0_dp
41 x(1, 1, 2) = -0.5_dp
42 x(2, 2, 2) = 0.5_dp
43 x(3, 3, 2) = -0.5_dp
44 x(4, 4, 2) = 0.5_dp
45 x(:, :, 3) = 2*x(:, :, 1) - x(:, :, 2)
46 CALL projected_current(h, s, x, e, j, status)
47 IF (status /= 0) error stop 'Spinor current failed'
48 IF (maxval(abs(e - [-0.1_dp, -0.1_dp, 0.9_dp, 0.9_dp])) > 1.e-12_dp) error stop 'Spinor spectrum failed'
49 DO a = 1, 3
50 IF (maxval(abs(j(:, :, a) + conjg(transpose(j(:, :, a))))) > 1.e-12_dp) error stop 'Current is not anti-Hermitian'
51 DO c = 1, 3
52 norm(a, c) = sum(conjg(j(:, :, a))*j(:, :, c))
53 END DO
54 END DO
55 IF (abs(norm(1, 1) - 4.0_dp) > 1.e-12_dp) error stop 'Analytic current strength failed'
56 ! Complex nonorthogonal basis mixing spin and orbitals, including a degenerate subspace.
57 b = s
58 b(1, 1) = 1.7_dp
59 b(2, 2) = 0.8_dp
60 b(3, 1) = cmplx(0.2_dp, 0.4_dp, dp)
61 b(1, 4) = cmplx(-0.3_dp, 0.1_dp, dp)
62 b(2, 3) = 0.4_dp
63 h2 = matmul(conjg(transpose(b)), matmul(h, b))
64 s2 = matmul(conjg(transpose(b)), b)
65 DO a = 1, 3
66 x2(:, :, a) = matmul(conjg(transpose(b)), matmul(x(:, :, a), b)) + real(a, dp)*s2
67 END DO
68 CALL projected_current(h2, s2, x2, e2, j2, status)
69 IF (status /= 0 .OR. maxval(abs(e - e2)) > 1.e-12_dp) error stop 'Nonorthogonal current spectrum failed'
70 DO a = 1, 3
71 DO c = 1, 3
72 norm2(a, c) = sum(conjg(j2(:, :, a))*j2(:, :, c))
73 END DO
74 END DO
75 IF (maxval(abs(norm - norm2)) > 1.e-11_dp) error stop 'Basis/origin/gauge invariance failed'
76 s(1, 1) = -1.0_dp
77 CALL projected_current(h, s, x, e, j, status)
78 IF (status /= -2) error stop 'Indefinite overlap accepted'
79 s(1, 1) = 1.0_dp
80 x(1, 2, 1) = 2.0_dp
81 CALL projected_current(h, s, x, e, j, status)
82 IF (status /= -1) error stop 'Non-Hermitian position accepted'
83 print *, 'Projected spinor current, metric and gauge tests passed.'
84 END SUBROUTINE check_projected_current
85
86! **************************************************************************************************
87!> \brief Validate Bloch currents, degenerate blocks and k-dependent AO gauges.
88! **************************************************************************************************
90 USE kinds, ONLY: dp
91 USE kubo_bloch, ONLY: bloch_current, &
95 COMPLEX(dp), PARAMETER :: iu = (0.0_dp, 1.0_dp)
96
97 COMPLEX(dp) :: b(2, 2), d(2, 2, 3), d2(2, 2, 3), db(2, 2), dh(2, 2, 3), dh2(2, 2, 3), &
98 ds(2, 2, 3), ds2(2, 2, 3), expected(2, 2), frame(2, 2), h(2, 2), h2(2, 2), j(2, 2, 3), &
99 j2(2, 2, 3), s(2, 2), s2(2, 2)
100 INTEGER :: i, ik, status
101 REAL(dp) :: e(2), e2(2), k, residuals(2), root, &
102 shift, speed, strength, strength2
103
104 s = 0.0_dp
105 s(1, 1) = 1.0_dp
106 s(2, 2) = 1.0_dp
107 DO ik = -7, 7
108 k = real(ik, dp)/5
109 h = k*k*s
110 h(1, 1) = h(1, 1) + 0.3_dp
111 h(2, 2) = h(2, 2) - 0.3_dp
112 h(1, 2) = 0.4_dp*sin(k) - iu*0.2_dp*cos(k)
113 h(2, 1) = conjg(h(1, 2))
114 dh = 0.0_dp
115 dh(:, :, 1) = 2*k*s
116 dh(1, 2, 1) = 0.4_dp*cos(k) + iu*0.2_dp*sin(k)
117 dh(2, 1, 1) = conjg(dh(1, 2, 1))
118 ds = 0.0_dp
119 d = 0.0_dp
120 CALL bloch_current(h, s, dh, ds, d, e, j, status, frame)
121 IF (status /= 0) error stop 'Bloch current failed'
122 CALL bloch_current_from_frame(h, s, dh, ds, d, e, frame, j2, 1.e-9_dp, residuals, status)
123 IF (status /= 0 .OR. maxval(abs(j2 - j)) > 1.e-12_dp) error stop 'Reused eigenframe failed'
124 CALL bloch_current_from_frame(h, s, dh, ds, d, e + 0.1_dp, frame, j2, 1.e-9_dp, residuals, status)
125 IF (status /= -4) error stop 'Wrong frame eigenvalues accepted'
126 CALL bloch_current_from_frame(h, s, dh, ds, d, e, 1.1_dp*frame, j2, 1.e-9_dp, residuals, status)
127 IF (status /= -4) error stop 'Nonorthonormal frame accepted'
128 root = sqrt(0.09_dp + 0.16_dp*sin(k)**2 + 0.04_dp*cos(k)**2)
129 IF (maxval(abs(e - [k*k - root, k*k + root])) > 1.e-12_dp) error stop 'Analytic bands failed'
130 speed = 0.12_dp*sin(k)*cos(k)/root
131 IF (abs(iu*j(1, 1, 1) - (2*k - speed)) > 1.e-12_dp) error stop 'Lower band velocity failed'
132 IF (abs(iu*j(2, 2, 1) - (2*k + speed)) > 1.e-12_dp) error stop 'Upper band velocity failed'
133 IF (maxval(abs(j(:, :, 1) + conjg(transpose(j(:, :, 1))))) > 1.e-12_dp) THEN
134 error stop 'Bloch current not anti-Hermitian'
135 END IF
136
137 ! A k-dependent nonunitary AO change requires both overlap and connection derivatives.
138 db = 0.0_dp
139 db(1, 1) = 0.13_dp
140 db(2, 1) = cmplx(0.17_dp, 0.23_dp, dp)
141 db(1, 2) = cmplx(-0.09_dp, 0.11_dp, dp)
142 b = s + k*db
143 h2 = matmul(conjg(transpose(b)), matmul(h, b))
144 s2 = matmul(conjg(transpose(b)), b)
145 dh2 = 0.0_dp
146 ds2 = 0.0_dp
147 d2 = 0.0_dp
148 dh2(:, :, 1) = matmul(conjg(transpose(b)), matmul(dh(:, :, 1), b)) + &
149 matmul(conjg(transpose(db)), matmul(h, b)) + matmul(conjg(transpose(b)), matmul(h, db))
150 ds2(:, :, 1) = matmul(conjg(transpose(db)), b) + matmul(conjg(transpose(b)), db)
151 d2(:, :, 1) = -iu*matmul(conjg(transpose(db)), b) + 7*s2
152 CALL bloch_current(h2, s2, dh2, ds2, d2, e2, j2, status)
153 IF (status /= 0 .OR. maxval(abs(e - e2)) > 1.e-12_dp) error stop 'Bloch basis covariance failed'
154 DO i = 1, 2
155 IF (abs(j(i, i, 1) - j2(i, i, 1)) > 1.e-11_dp) error stop 'Band velocity basis dependence'
156 END DO
157 IF (maxval(abs(abs(j) - abs(j2))) > 1.e-11_dp) error stop 'Interband velocity gauge dependence'
158 ! An energy-zero shift must include the k derivative of the metric.
159 shift = 4.2_dp
160 CALL bloch_current(h2 + shift*s2, s2, dh2 + shift*ds2, ds2, d2, e2, j2, status)
161 IF (status /= 0 .OR. maxval(abs(e + shift - e2)) > 1.e-11_dp) error stop 'Energy shift failed'
162 IF (maxval(abs(abs(j) - abs(j2))) > 1.e-10_dp) error stop 'Velocity energy-zero dependence'
163 d2(:, :, 1) = 0.0_dp
164 CALL bloch_current(h2, s2, dh2, ds2, d2, e2, j2, status)
165 IF (status /= -3) error stop 'Inconsistent connection accepted'
166 END DO
167
168 dh = 0.0_dp
169 ds = 0.0_dp
170 d = 0.0_dp
171 d(1, 2, 1) = iu
172 d(2, 1, 1) = -iu
173 CALL bloch_current(h, s, dh, ds, d, e, j, status)
174 IF (status /= 0) error stop 'Finite Bloch limit failed'
175 CALL projected_current(h, s, d, e2, j2, status)
176 IF (status /= 0 .OR. maxval(abs(j - j2)) > 1.e-12_dp) error stop 'Finite commutator limit failed'
177
178 ! Every matrix element within an exactly degenerate block has the same Fermi weight.
179 h = 0.0_dp
180 dh = 0.0_dp
181 d = 0.0_dp
182 dh(:, :, 1) = s
183 dh(1, 2, 1) = iu
184 dh(2, 1, 1) = -iu
185 CALL bloch_current(h, s, dh, ds, d, e, j, status)
186 IF (status /= 0) error stop 'Degenerate Bloch block failed'
187 strength = sum(abs(j(:, :, 1))**2)*(-fermi_slope(0.0_dp, 0.0_dp, 0.0_dp, 0.1_dp))
188 IF (abs(strength - 10.0_dp) > 1.e-12_dp) error stop 'Intraband Drude weight failed'
189 b = reshape([iu, (1.0_dp, 0.0_dp), (1.0_dp, 0.0_dp), iu], [2, 2])/sqrt(2.0_dp)
190 expected = matmul(conjg(transpose(b)), matmul(dh(:, :, 1), b))
191 dh(:, :, 1) = expected
192 CALL bloch_current(h, s, dh, ds, d, e, j, status)
193 strength2 = sum(abs(j(:, :, 1))**2)*(-fermi_slope(0.0_dp, 0.0_dp, 0.0_dp, 0.1_dp))
194 IF (status /= 0 .OR. abs(strength - strength2) > 1.e-12_dp) error stop 'Degenerate response gauge failed'
195 IF (abs(fermi_slope(1.e-9_dp, -1.e-9_dp, 0.0_dp, 0.1_dp) + 2.5_dp) > 1.e-12_dp) THEN
196 error stop 'Fermi near-degeneracy limit failed'
197 END IF
198 IF (abs(fermi_slope(0.1_dp, -0.1_dp, 0.0_dp, 0.1_dp) + tanh(0.5_dp)/0.2_dp) > 1.e-12_dp) THEN
199 error stop 'Fermi divided difference failed'
200 END IF
201 print *, 'Bloch metric/connection, band velocity, gauge, degeneracy and Drude tests passed.'
202 END SUBROUTINE check_bloch_current
203
204! **************************************************************************************************
205!> \brief Validate Hall signs against Liouvillian and Berry-curvature references.
206! **************************************************************************************************
208 USE ieee_arithmetic, ONLY: ieee_quiet_nan, &
209 ieee_value
210 USE kinds, ONLY: dp
211 USE kubo_bloch, ONLY: bloch_current, &
213 USE kubo_hall, ONLY: hall_response
214
215 IMPLICIT NONE
216 COMPLEX(dp), PARAMETER :: iu = (0.0_dp, 1.0_dp)
217 REAL(dp), PARAMETER :: pi = 3.1415926535897932384626433832795_dp
218 REAL(dp) :: energies(4), hall(3, 3), check(3, 3), reference(3, 3), rotation(3, 3)
219 REAL(dp) :: analytic, error, gauge_error, mass, kx, ky, radius, omega, integrated, curvature, gamma
220 REAL(dp) :: e2(2), masses(3), expected(3), unit(3)
221 COMPLEX(dp) :: current(4, 4, 3), changed(4, 4, 3), u(4, 4)
222 COMPLEX(dp) :: h2(2, 2), s2(2, 2), dh2(2, 2, 3), ds2(2, 2, 3), d2(2, 2, 3), j2(2, 2, 3)
223 INTEGER :: a, b, i, j, imass, ix, iy, status
224 INTEGER, PARAMETER :: mesh = 48
225
226 ! A two-level response fixes the sign, occupation factor and relaxation denominator.
227 j2 = 0.0_dp
228 j2(1, 2, 1) = -iu
229 j2(2, 1, 1) = -iu
230 j2(1, 2, 2) = -1.0_dp
231 j2(2, 1, 2) = 1.0_dp
232 CALL hall_response([-0.7_dp, 0.7_dp], j2, 0.0_dp, 0.2_dp, 0.3_dp, hall, status)
233 analytic = 2.0_dp*tanh(0.7_dp/0.4_dp)/(1.4_dp**2 + 0.3_dp**2)
234 IF (status /= 0 .OR. abs(hall(1, 2) - analytic) > 1.e-13_dp) error stop 'Two-level Hall sign or scale'
235 IF (maxval(abs(hall + transpose(hall))) /= 0.0_dp) error stop 'Hall not antisymmetric'
236 CALL hall_response([0.0_dp, 0.0_dp], j2, 0.0_dp, 0.2_dp, 0.3_dp, hall, status)
237 IF (status /= 0 .OR. maxval(abs(hall)) /= 0.0_dp) error stop 'Degenerate Hall block'
238 DO i = 2, 14, 2
239 radius = 10.0_dp**(-i)
240 CALL hall_response([-radius, radius], j2, 0.0_dp, 0.2_dp, 0.3_dp, hall, status)
241 analytic = 2.0_dp*tanh(radius/0.4_dp)/(4*radius**2 + 0.3_dp**2)
242 IF (status /= 0 .OR. abs(hall(1, 2)/analytic - 1.0_dp) > 1.e-12_dp) THEN
243 error stop 'Near-degenerate Hall limit'
244 END IF
245 END DO
246
247 energies = [-0.9_dp, -0.9_dp, 0.8_dp, 0.8_dp]
248 DO a = 1, 3
249 DO j = 1, 4
250 DO i = 1, 4
251 current(i, j, a) = cmplx(sin(real(i + 3*j + a, dp)), cos(real(2*i - j + a, dp)), dp)
252 END DO
253 END DO
254 current(:, :, a) = current(:, :, a) - conjg(transpose(current(:, :, a)))
255 END DO
256 CALL hall_response(energies, current, 0.1_dp, 0.15_dp, 0.07_dp, hall, status)
257 IF (status /= 0 .OR. maxval(abs(hall)) < 0.1_dp) error stop 'Nontrivial four-band response'
258 CALL liouville_reference(energies, current, 0.1_dp, 0.15_dp, 0.07_dp, reference)
259 error = maxval(abs(hall - 0.5_dp*(reference - transpose(reference))))
260 IF (error > 2.e-12_dp) error stop 'Full Liouvillian reference mismatch'
261 print '(A,ES14.6)', 'Hall Liouvillian residual: ', error
262 DO a = 1, 3
263 unit = 0.0_dp
264 unit(a) = 1.0_dp
265 IF (dot_product(unit, matmul(reference, unit)) < -1.e-12_dp) error stop 'Dissipation sign'
266 END DO
267
268 u = 0.0_dp
269 u(1:2, 1:2) = reshape([iu, (1.0_dp, 0.0_dp), (1.0_dp, 0.0_dp), iu], [2, 2])/sqrt(2.0_dp)
270 u(3:4, 3:4) = reshape([(0.6_dp, 0.0_dp), (0.0_dp, 0.8_dp), (0.0_dp, 0.8_dp), &
271 (0.6_dp, 0.0_dp)], [2, 2])
272 DO a = 1, 3
273 changed(:, :, a) = matmul(conjg(transpose(u)), matmul(current(:, :, a), u))
274 END DO
275 CALL hall_response(energies, changed, 0.1_dp, 0.15_dp, 0.07_dp, check, status)
276 IF (status /= 0 .OR. maxval(abs(hall - check)) > 2.e-12_dp) error stop 'Degenerate gauge covariance'
277 CALL hall_response(energies + 2.3_dp, current, 2.4_dp, 0.15_dp, 0.07_dp, check, status)
278 IF (status /= 0 .OR. maxval(abs(hall - check)) > 2.e-12_dp) error stop 'Energy zero covariance'
279 changed = current([3, 1, 4, 2], [3, 1, 4, 2], :)
280 CALL hall_response(energies([3, 1, 4, 2]), changed, 0.1_dp, 0.15_dp, 0.07_dp, check, status)
281 IF (status /= 0 .OR. maxval(abs(hall - check)) > 2.e-12_dp) error stop 'Band permutation covariance'
282 CALL hall_response(energies, conjg(current), 0.1_dp, 0.15_dp, 0.07_dp, check, status)
283 IF (status /= 0 .OR. maxval(abs(hall + check)) > 2.e-12_dp) error stop 'Time-reversed Hall sign'
284 rotation = reshape([0.6_dp, 0.8_dp, 0.0_dp, -0.8_dp, 0.6_dp, 0.0_dp, &
285 0.0_dp, 0.0_dp, -1.0_dp], [3, 3])
286 changed = 0.0_dp
287 DO a = 1, 3
288 DO b = 1, 3
289 changed(:, :, a) = changed(:, :, a) + rotation(a, b)*current(:, :, b)
290 END DO
291 END DO
292 CALL hall_response(energies, changed, 0.1_dp, 0.15_dp, 0.07_dp, check, status)
293 reference = matmul(rotation, matmul(hall, transpose(rotation)))
294 IF (status /= 0 .OR. maxval(abs(reference - check)) > 2.e-12_dp) error stop 'Cartesian tensor covariance'
295
296 CALL hall_response(energies, current, 0.0_dp, 0.0_dp, 0.1_dp, check, status)
297 IF (status == 0) error stop 'Zero temperature accepted'
298 CALL hall_response(energies, current, 0.0_dp, 0.1_dp, 0.0_dp, check, status)
299 IF (status == 0) error stop 'Zero relaxation accepted'
300 CALL hall_response(energies, current, ieee_value(0.0_dp, ieee_quiet_nan), 0.1_dp, 0.1_dp, check, status)
301 IF (status == 0) error stop 'NaN chemical potential accepted'
302 changed = current
303 changed(1, 2, 1) = changed(1, 2, 1) + 0.1_dp
304 CALL hall_response(energies, changed, 0.0_dp, 0.1_dp, 0.1_dp, check, status)
305 IF (status == 0) error stop 'Non-anti-Hermitian current accepted'
306
307 ! Two-band lattice model: compare every point against analytic Berry curvature,
308 ! then integrate its two opposite Chern phases and a trivial phase.
309 masses = [-1.0_dp, 1.0_dp, 3.0_dp]
310 expected = [-1.0_dp, 1.0_dp, 0.0_dp]
311 s2 = 0.0_dp
312 s2(1, 1) = 1.0_dp
313 s2(2, 2) = 1.0_dp
314 ds2 = 0.0_dp
315 d2 = 0.0_dp
316 gamma = 1.e-3_dp
317 DO imass = 1, 3
318 mass = masses(imass)
319 integrated = 0.0_dp
320 curvature = 0.0_dp
321 error = 0.0_dp
322 gauge_error = 0.0_dp
323 DO iy = 0, mesh - 1
324 ky = 2*pi*real(iy, dp)/mesh
325 DO ix = 0, mesh - 1
326 kx = 2*pi*real(ix, dp)/mesh
327 h2(1, 1) = mass + cos(kx) + cos(ky)
328 h2(2, 2) = -h2(1, 1)
329 h2(1, 2) = sin(kx) - iu*sin(ky)
330 h2(2, 1) = conjg(h2(1, 2))
331 dh2 = 0.0_dp
332 dh2(1, 1, 1) = -sin(kx)
333 dh2(2, 2, 1) = sin(kx)
334 dh2(1, 2, 1) = cos(kx)
335 dh2(2, 1, 1) = cos(kx)
336 dh2(1, 1, 2) = -sin(ky)
337 dh2(2, 2, 2) = sin(ky)
338 dh2(1, 2, 2) = -iu*cos(ky)
339 dh2(2, 1, 2) = iu*cos(ky)
340 CALL bloch_current(h2, s2, dh2, ds2, d2, e2, j2, status)
341 IF (status /= 0) error stop 'Chern-model Bloch current'
342 CALL hall_response(e2, j2, 0.0_dp, 0.01_dp, gamma, check, status)
343 IF (status /= 0) error stop 'Chern-model Hall kernel'
344 radius = sqrt(sin(kx)**2 + sin(ky)**2 + (mass + cos(kx) + cos(ky))**2)
345 omega = (cos(kx) + cos(ky) + mass*cos(kx)*cos(ky))/(2*radius**3)
346 analytic = -omega*4*radius**2/(4*radius**2 + gamma**2)*tanh(radius/0.02_dp)
347 error = max(error, abs(check(1, 2) - analytic))
348 CALL ao_gauge_reference(kx, ky, h2, dh2, gamma, reference)
349 gauge_error = max(gauge_error, maxval(abs(check - reference)))
350 integrated = integrated + check(1, 2)*2*pi/mesh**2
351 curvature = curvature + omega*2*pi/mesh**2
352 END DO
353 END DO
354 IF (error > 1.e-12_dp) error stop 'Analytic Berry-curvature response mismatch'
355 IF (abs(integrated - expected(imass)) > 1.e-6_dp) error stop 'Chern Hall limit'
356 IF (abs(curvature + expected(imass)) > 1.e-10_dp) error stop 'Independent Chern integral'
357 IF (gauge_error > 1.e-12_dp) error stop 'Nonorthogonal AO Hall covariance'
358 print '(A,F5.1,3ES18.10)', 'Hall Chern model mass, sigma, Chern, residual: ', mass, integrated, curvature, error
359 print '(A,ES14.6)', 'Hall k-dependent nonorthogonal AO residual: ', gauge_error
360 END DO
361 print *, 'Hall sign, Liouvillian, gauge, time reversal, degeneracy and Chern tests passed.'
362 CONTAINS
363! **************************************************************************************************
364!> \brief Transform the lattice model to a k-dependent nonorthogonal AO frame.
365!> \param kx First wave-vector component
366!> \param ky Second wave-vector component
367!> \param h Orthogonal Hamiltonian
368!> \param dh Its Cartesian derivatives
369!> \param gamma Relaxation energy
370!> \param hall Hall response in the transformed AO frame
371! **************************************************************************************************
372 SUBROUTINE ao_gauge_reference(kx, ky, h, dh, gamma, hall)
373 REAL(dp), INTENT(IN) :: kx, ky
374 COMPLEX(dp), INTENT(IN) :: h(2, 2), dh(2, 2, 3)
375 REAL(dp), INTENT(IN) :: gamma
376 REAL(dp), INTENT(OUT) :: hall(3, 3)
377
378 COMPLEX(dp) :: basis(2, 2), db(2, 2, 3), dhx(2, 2, 3), &
379 dsx(2, 2, 3), dx(2, 2, 3), hx(2, 2), &
380 jx(2, 2, 3), sx(2, 2)
381 INTEGER :: a, status
382 REAL(dp) :: e(2)
383
384 basis = 0.0_dp
385 basis(1, 1) = 1.1_dp + 0.1_dp*cos(kx)
386 basis(2, 2) = 0.9_dp + 0.1_dp*sin(ky)
387 basis(1, 2) = 0.13_dp*exp(iu*(kx + ky))
388 basis(2, 1) = 0.07_dp*exp(iu*(2*kx - ky))
389 db = 0.0_dp
390 db(1, 1, 1) = -0.1_dp*sin(kx)
391 db(2, 2, 2) = 0.1_dp*cos(ky)
392 db(1, 2, 1:2) = iu*basis(1, 2)
393 db(2, 1, 1) = 2*iu*basis(2, 1)
394 db(2, 1, 2) = -iu*basis(2, 1)
395 hx = matmul(conjg(transpose(basis)), matmul(h, basis))
396 sx = matmul(conjg(transpose(basis)), basis)
397 DO a = 1, 3
398 dhx(:, :, a) = matmul(conjg(transpose(basis)), matmul(dh(:, :, a), basis)) + &
399 matmul(conjg(transpose(db(:, :, a))), matmul(h, basis)) + &
400 matmul(conjg(transpose(basis)), matmul(h, db(:, :, a)))
401 dsx(:, :, a) = matmul(conjg(transpose(db(:, :, a))), basis) + &
402 matmul(conjg(transpose(basis)), db(:, :, a))
403 dx(:, :, a) = -iu*matmul(conjg(transpose(db(:, :, a))), basis) + real(a, dp)*sx
404 END DO
405 CALL bloch_current(hx, sx, dhx, dsx, dx, e, jx, status)
406 IF (status /= 0) error stop 'Nonorthogonal AO Chern model'
407 CALL hall_response(e, jx, 0.0_dp, 0.01_dp, gamma, hall, status)
408 IF (status /= 0) error stop 'Nonorthogonal AO Hall kernel'
409 END SUBROUTINE ao_gauge_reference
410
411! **************************************************************************************************
412!> \brief Invert the full complex Liouvillian in a nondiagonal four-level frame.
413!> \param e Eigenvalues
414!> \param currents Anti-Hermitian currents in the diagonal frame
415!> \param mu Chemical potential
416!> \param kt Temperature
417!> \param gamma Relaxation rate
418!> \param sigma Complete real DC tensor without physical prefactors
419! **************************************************************************************************
420 SUBROUTINE liouville_reference(e, currents, mu, kt, gamma, sigma)
421 REAL(dp), INTENT(IN) :: e(4)
422 COMPLEX(dp), INTENT(IN) :: currents(4, 4, 3)
423 REAL(dp), INTENT(IN) :: mu, kt, gamma
424 REAL(dp), INTENT(OUT) :: sigma(3, 3)
425
426 COMPLEX(dp) :: basis(4, 4), drive(4, 4), h(4, 4), &
427 liouvillian(16, 16), response(4, 4), &
428 rhs(16, 3), trace, v(4, 4, 3)
429 INTEGER :: a, b, column, i, info, j, k, l, &
430 pivot(16), row
431
432 DO j = 1, 4
433 DO i = 1, 4
434 basis(i, j) = exp(iu*2*pi*real((i - 1)*(j - 1), dp)/4)/2
435 END DO
436 END DO
437 h = 0.0_dp
438 DO i = 1, 4
439 h(i, i) = e(i)
440 END DO
441 h = matmul(basis, matmul(h, conjg(transpose(basis))))
442 DO a = 1, 3
443 v(:, :, a) = matmul(basis, matmul(iu*currents(:, :, a), conjg(transpose(basis))))
444 DO j = 1, 4
445 DO i = 1, 4
446 drive(i, j) = fermi_slope(e(i), e(j), mu, kt)*iu*currents(i, j, a)
447 END DO
448 END DO
449 drive = matmul(basis, matmul(drive, conjg(transpose(basis))))
450 rhs(:, a) = reshape(drive, [16])
451 END DO
452 liouvillian = 0.0_dp
453 DO j = 1, 4
454 DO i = 1, 4
455 row = i + 4*(j - 1)
456 liouvillian(row, row) = gamma
457 DO l = 1, 4
458 DO k = 1, 4
459 column = k + 4*(l - 1)
460 IF (j == l) liouvillian(row, column) = liouvillian(row, column) + iu*h(i, k)
461 IF (i == k) liouvillian(row, column) = liouvillian(row, column) - iu*h(l, j)
462 END DO
463 END DO
464 END DO
465 END DO
466 CALL zgesv(16, 3, liouvillian, 16, pivot, rhs, 16, info)
467 IF (info /= 0) error stop 'Liouvillian solve failed'
468 DO b = 1, 3
469 response = reshape(rhs(:, b), [4, 4])
470 DO a = 1, 3
471 trace = sum(v(:, :, a)*transpose(response))
472 sigma(a, b) = -real(trace, dp)
473 END DO
474 END DO
475 END SUBROUTINE liouville_reference
476 END SUBROUTINE check_hall_response
477
478! **************************************************************************************************
479!> \brief Periodic QWZ phases, physical windows, dual gauges, nonorthogonal bases and spinor TR.
480
481END MODULE kubo_test_cases
482
485 IMPLICIT NONE
489END PROGRAM kubo_unittest
program kubo_unittest
subroutine ao_gauge_reference(kx, ky, h, dh, gamma, hall)
Transform the lattice model to a k-dependent nonorthogonal AO frame.
Calculation of the incomplete Gamma function F_n(t) for multi-center integrals over Cartesian Gaussia...
Definition gamma.F:15
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Metric-aware Bloch velocities without eigenvector derivatives or band-gap divisions.
Definition kubo_bloch.F:12
subroutine, public bloch_current(h, s, dh, ds, d, energies, current, status, coefficients)
Evaluate v_nm = C^H(dH - E_n*dS)C + i*(E_n-E_m)*C^H*D*C.
Definition kubo_bloch.F:36
subroutine, public bloch_current_from_frame(h, s, dh, ds, d, energies, c, current, tolerance, residuals, status)
Evaluate the current in a validated reconstructed eigenframe, without rediagonalization.
Definition kubo_bloch.F:76
pure real(dp) function, public fermi_slope(ea, eb, mu, kt)
Stable Fermi divided difference including the intraband and degenerate limits.
Definition kubo_bloch.F:163
Antisymmetric DC charge response with a scalar relaxation rate.
Definition kubo_hall.F:12
subroutine, public hall_response(energies, current, mu, kt, gamma, hall, status)
Hall tensor, with first index the current and second index the electric field.
Definition kubo_hall.F:37
Finite projected-Hamiltonian current in a nonorthogonal, possibly spinor AO basis.
subroutine, public projected_current(h, s, positions, energies, current, status)
Compute C^dagger (H S^-1 X - X S^-1 H) C without forming S^-1.
subroutine, public check_hall_response()
Validate Hall signs against Liouvillian and Berry-curvature references.
subroutine, public check_bloch_current()
Validate Bloch currents, degenerate blocks and k-dependent AO gauges.
subroutine, public check_projected_current()
Validate projected currents and their AO-metric covariance.
subroutine check(condition, label)
Stops with an error message if a test fails.