LCOV - code coverage report
Current view: top level - src - kubo_unittest.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 98.8 % 322 318
Test Date: 2026-10-15 00:55:44 Functions: 100.0 % 7 7

            Line data    Source code
       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              : !--------------------------------------------------------------------------------------------------!
       7              : MODULE kubo_test_cases
       8              :    IMPLICIT NONE
       9              :    PRIVATE
      10              :    PUBLIC :: check_projected_current, check_bloch_current, check_hall_response
      11              : CONTAINS
      12              : 
      13              : ! **************************************************************************************************
      14              : !> \brief Validate projected currents and their AO-metric covariance.
      15              : ! **************************************************************************************************
      16            2 :    SUBROUTINE check_projected_current()
      17              :       USE kinds, ONLY: dp
      18              :       USE kubo_projected, ONLY: projected_current
      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            2 :       h = 0.0_dp
      25            2 :       s = 0.0_dp
      26            2 :       x = 0.0_dp
      27           10 :       DO i = 1, 4
      28           10 :          s(i, i) = 1.0_dp
      29              :       END DO
      30              :       ! Kramers-degenerate spinor model with imaginary SOC matrix elements.
      31            2 :       h(2, 2) = 0.8_dp
      32            2 :       h(4, 4) = 0.8_dp
      33            2 :       h(1, 2) = CMPLX(0.0_dp, 0.3_dp, dp)
      34            2 :       h(2, 1) = CONJG(h(1, 2))
      35            2 :       h(3, 4) = CONJG(h(1, 2))
      36            2 :       h(4, 3) = h(1, 2)
      37            2 :       x(1, 2, 1) = 1.0_dp
      38            2 :       x(2, 1, 1) = 1.0_dp
      39            2 :       x(3, 4, 1) = 1.0_dp
      40            2 :       x(4, 3, 1) = 1.0_dp
      41            2 :       x(1, 1, 2) = -0.5_dp
      42            2 :       x(2, 2, 2) = 0.5_dp
      43            2 :       x(3, 3, 2) = -0.5_dp
      44            2 :       x(4, 4, 2) = 0.5_dp
      45           42 :       x(:, :, 3) = 2*x(:, :, 1) - x(:, :, 2)
      46            2 :       CALL projected_current(h, s, x, e, j, status)
      47            2 :       IF (status /= 0) ERROR STOP 'Spinor current failed'
      48           10 :       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            8 :       DO a = 1, 3
      50          126 :          IF (MAXVAL(ABS(j(:, :, a) + CONJG(TRANSPOSE(j(:, :, a))))) > 1.e-12_dp) ERROR STOP 'Current is not anti-Hermitian'
      51           26 :          DO c = 1, 3
      52          384 :             norm(a, c) = SUM(CONJG(j(:, :, a))*j(:, :, c))
      53              :          END DO
      54              :       END DO
      55            2 :       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            2 :       b = s
      58            2 :       b(1, 1) = 1.7_dp
      59            2 :       b(2, 2) = 0.8_dp
      60            2 :       b(3, 1) = CMPLX(0.2_dp, 0.4_dp, dp)
      61            2 :       b(1, 4) = CMPLX(-0.3_dp, 0.1_dp, dp)
      62            2 :       b(2, 3) = 0.4_dp
      63          338 :       h2 = MATMUL(CONJG(TRANSPOSE(b)), MATMUL(h, b))
      64          170 :       s2 = MATMUL(CONJG(TRANSPOSE(b)), b)
      65            8 :       DO a = 1, 3
      66         1256 :          x2(:, :, a) = MATMUL(CONJG(TRANSPOSE(b)), MATMUL(x(:, :, a), b)) + REAL(a, dp)*s2
      67              :       END DO
      68            2 :       CALL projected_current(h2, s2, x2, e2, j2, status)
      69           10 :       IF (status /= 0 .OR. MAXVAL(ABS(e - e2)) > 1.e-12_dp) ERROR STOP 'Nonorthogonal current spectrum failed'
      70            8 :       DO a = 1, 3
      71           26 :          DO c = 1, 3
      72          384 :             norm2(a, c) = SUM(CONJG(j2(:, :, a))*j2(:, :, c))
      73              :          END DO
      74              :       END DO
      75           26 :       IF (MAXVAL(ABS(norm - norm2)) > 1.e-11_dp) ERROR STOP 'Basis/origin/gauge invariance failed'
      76            2 :       s(1, 1) = -1.0_dp
      77            2 :       CALL projected_current(h, s, x, e, j, status)
      78            2 :       IF (status /= -2) ERROR STOP 'Indefinite overlap accepted'
      79            2 :       s(1, 1) = 1.0_dp
      80            2 :       x(1, 2, 1) = 2.0_dp
      81            2 :       CALL projected_current(h, s, x, e, j, status)
      82            2 :       IF (status /= -1) ERROR STOP 'Non-Hermitian position accepted'
      83            2 :       PRINT *, 'Projected spinor current, metric and gauge tests passed.'
      84            2 :    END SUBROUTINE check_projected_current
      85              : 
      86              : ! **************************************************************************************************
      87              : !> \brief Validate Bloch currents, degenerate blocks and k-dependent AO gauges.
      88              : ! **************************************************************************************************
      89            2 :    SUBROUTINE check_bloch_current()
      90            2 :       USE kinds, ONLY: dp
      91              :       USE kubo_bloch, ONLY: bloch_current, &
      92              :                             bloch_current_from_frame, &
      93              :                             fermi_slope
      94              :       USE kubo_projected, ONLY: projected_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            2 :       s = 0.0_dp
     105            2 :       s(1, 1) = 1.0_dp
     106            2 :       s(2, 2) = 1.0_dp
     107           32 :       DO ik = -7, 7
     108           30 :          k = REAL(ik, dp)/5
     109          210 :          h = k*k*s
     110           30 :          h(1, 1) = h(1, 1) + 0.3_dp
     111           30 :          h(2, 2) = h(2, 2) - 0.3_dp
     112           30 :          h(1, 2) = 0.4_dp*SIN(k) - iu*0.2_dp*COS(k)
     113           30 :          h(2, 1) = CONJG(h(1, 2))
     114           30 :          dh = 0.0_dp
     115          210 :          dh(:, :, 1) = 2*k*s
     116           30 :          dh(1, 2, 1) = 0.4_dp*COS(k) + iu*0.2_dp*SIN(k)
     117           30 :          dh(2, 1, 1) = CONJG(dh(1, 2, 1))
     118           30 :          ds = 0.0_dp
     119           30 :          d = 0.0_dp
     120           30 :          CALL bloch_current(h, s, dh, ds, d, e, j, status, frame)
     121           30 :          IF (status /= 0) ERROR STOP 'Bloch current failed'
     122           30 :          CALL bloch_current_from_frame(h, s, dh, ds, d, e, frame, j2, 1.e-9_dp, residuals, status)
     123          660 :          IF (status /= 0 .OR. MAXVAL(ABS(j2 - j)) > 1.e-12_dp) ERROR STOP 'Reused eigenframe failed'
     124           90 :          CALL bloch_current_from_frame(h, s, dh, ds, d, e + 0.1_dp, frame, j2, 1.e-9_dp, residuals, status)
     125           30 :          IF (status /= -4) ERROR STOP 'Wrong frame eigenvalues accepted'
     126          210 :          CALL bloch_current_from_frame(h, s, dh, ds, d, e, 1.1_dp*frame, j2, 1.e-9_dp, residuals, status)
     127           30 :          IF (status /= -4) ERROR STOP 'Nonorthonormal frame accepted'
     128           30 :          root = SQRT(0.09_dp + 0.16_dp*SIN(k)**2 + 0.04_dp*COS(k)**2)
     129           90 :          IF (MAXVAL(ABS(e - [k*k - root, k*k + root])) > 1.e-12_dp) ERROR STOP 'Analytic bands failed'
     130           30 :          speed = 0.12_dp*SIN(k)*COS(k)/root
     131           30 :          IF (ABS(iu*j(1, 1, 1) - (2*k - speed)) > 1.e-12_dp) ERROR STOP 'Lower band velocity failed'
     132           30 :          IF (ABS(iu*j(2, 2, 1) - (2*k + speed)) > 1.e-12_dp) ERROR STOP 'Upper band velocity failed'
     133          210 :          IF (MAXVAL(ABS(j(:, :, 1) + CONJG(TRANSPOSE(j(:, :, 1))))) > 1.e-12_dp) THEN
     134            0 :             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           30 :          db = 0.0_dp
     139           30 :          db(1, 1) = 0.13_dp
     140           30 :          db(2, 1) = CMPLX(0.17_dp, 0.23_dp, dp)
     141           30 :          db(1, 2) = CMPLX(-0.09_dp, 0.11_dp, dp)
     142          210 :          b = s + k*db
     143          870 :          h2 = MATMUL(CONJG(TRANSPOSE(b)), MATMUL(h, b))
     144          450 :          s2 = MATMUL(CONJG(TRANSPOSE(b)), b)
     145           30 :          dh2 = 0.0_dp
     146           30 :          ds2 = 0.0_dp
     147           30 :          d2 = 0.0_dp
     148              :          dh2(:, :, 1) = MATMUL(CONJG(TRANSPOSE(b)), MATMUL(dh(:, :, 1), b)) + &
     149         2910 :                         MATMUL(CONJG(TRANSPOSE(db)), MATMUL(h, b)) + MATMUL(CONJG(TRANSPOSE(b)), MATMUL(h, db))
     150         1230 :          ds2(:, :, 1) = MATMUL(CONJG(TRANSPOSE(db)), b) + MATMUL(CONJG(TRANSPOSE(b)), db)
     151          810 :          d2(:, :, 1) = -iu*MATMUL(CONJG(TRANSPOSE(db)), b) + 7*s2
     152           30 :          CALL bloch_current(h2, s2, dh2, ds2, d2, e2, j2, status)
     153           90 :          IF (status /= 0 .OR. MAXVAL(ABS(e - e2)) > 1.e-12_dp) ERROR STOP 'Bloch basis covariance failed'
     154           90 :          DO i = 1, 2
     155           90 :             IF (ABS(j(i, i, 1) - j2(i, i, 1)) > 1.e-11_dp) ERROR STOP 'Band velocity basis dependence'
     156              :          END DO
     157          660 :          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           30 :          shift = 4.2_dp
     160          840 :          CALL bloch_current(h2 + shift*s2, s2, dh2 + shift*ds2, ds2, d2, e2, j2, status)
     161           90 :          IF (status /= 0 .OR. MAXVAL(ABS(e + shift - e2)) > 1.e-11_dp) ERROR STOP 'Energy shift failed'
     162          660 :          IF (MAXVAL(ABS(ABS(j) - ABS(j2))) > 1.e-10_dp) ERROR STOP 'Velocity energy-zero dependence'
     163          210 :          d2(:, :, 1) = 0.0_dp
     164           30 :          CALL bloch_current(h2, s2, dh2, ds2, d2, e2, j2, status)
     165           32 :          IF (status /= -3) ERROR STOP 'Inconsistent connection accepted'
     166              :       END DO
     167              : 
     168            2 :       dh = 0.0_dp
     169            2 :       ds = 0.0_dp
     170            2 :       d = 0.0_dp
     171            2 :       d(1, 2, 1) = iu
     172            2 :       d(2, 1, 1) = -iu
     173            2 :       CALL bloch_current(h, s, dh, ds, d, e, j, status)
     174            2 :       IF (status /= 0) ERROR STOP 'Finite Bloch limit failed'
     175            2 :       CALL projected_current(h, s, d, e2, j2, status)
     176           44 :       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            2 :       h = 0.0_dp
     180            2 :       dh = 0.0_dp
     181            2 :       d = 0.0_dp
     182           14 :       dh(:, :, 1) = s
     183            2 :       dh(1, 2, 1) = iu
     184            2 :       dh(2, 1, 1) = -iu
     185            2 :       CALL bloch_current(h, s, dh, ds, d, e, j, status)
     186            2 :       IF (status /= 0) ERROR STOP 'Degenerate Bloch block failed'
     187           14 :       strength = SUM(ABS(j(:, :, 1))**2)*(-fermi_slope(0.0_dp, 0.0_dp, 0.0_dp, 0.1_dp))
     188            2 :       IF (ABS(strength - 10.0_dp) > 1.e-12_dp) ERROR STOP 'Intraband Drude weight failed'
     189            2 :       b = RESHAPE([iu, (1.0_dp, 0.0_dp), (1.0_dp, 0.0_dp), iu], [2, 2])/SQRT(2.0_dp)
     190           58 :       expected = MATMUL(CONJG(TRANSPOSE(b)), MATMUL(dh(:, :, 1), b))
     191           14 :       dh(:, :, 1) = expected
     192            2 :       CALL bloch_current(h, s, dh, ds, d, e, j, status)
     193           14 :       strength2 = SUM(ABS(j(:, :, 1))**2)*(-fermi_slope(0.0_dp, 0.0_dp, 0.0_dp, 0.1_dp))
     194            2 :       IF (status /= 0 .OR. ABS(strength - strength2) > 1.e-12_dp) ERROR STOP 'Degenerate response gauge failed'
     195            2 :       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            0 :          ERROR STOP 'Fermi near-degeneracy limit failed'
     197              :       END IF
     198            2 :       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            0 :          ERROR STOP 'Fermi divided difference failed'
     200              :       END IF
     201            2 :       PRINT *, 'Bloch metric/connection, band velocity, gauge, degeneracy and Drude tests passed.'
     202            2 :    END SUBROUTINE check_bloch_current
     203              : 
     204              : ! **************************************************************************************************
     205              : !> \brief Validate Hall signs against Liouvillian and Berry-curvature references.
     206              : ! **************************************************************************************************
     207            2 :    SUBROUTINE check_hall_response()
     208            2 :       USE ieee_arithmetic, ONLY: ieee_quiet_nan, &
     209              :                                  ieee_value
     210              :       USE kinds, ONLY: dp
     211              :       USE kubo_bloch, ONLY: bloch_current, &
     212              :                             fermi_slope
     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            2 :       j2 = 0.0_dp
     228            2 :       j2(1, 2, 1) = -iu
     229            2 :       j2(2, 1, 1) = -iu
     230            2 :       j2(1, 2, 2) = -1.0_dp
     231            2 :       j2(2, 1, 2) = 1.0_dp
     232            2 :       CALL hall_response([-0.7_dp, 0.7_dp], j2, 0.0_dp, 0.2_dp, 0.3_dp, hall, status)
     233            2 :       analytic = 2.0_dp*TANH(0.7_dp/0.4_dp)/(1.4_dp**2 + 0.3_dp**2)
     234            2 :       IF (status /= 0 .OR. ABS(hall(1, 2) - analytic) > 1.e-13_dp) ERROR STOP 'Two-level Hall sign or scale'
     235           26 :       IF (MAXVAL(ABS(hall + TRANSPOSE(hall))) /= 0.0_dp) ERROR STOP 'Hall not antisymmetric'
     236            2 :       CALL hall_response([0.0_dp, 0.0_dp], j2, 0.0_dp, 0.2_dp, 0.3_dp, hall, status)
     237           26 :       IF (status /= 0 .OR. MAXVAL(ABS(hall)) /= 0.0_dp) ERROR STOP 'Degenerate Hall block'
     238           14 :       DO i = 2, 14, 2
     239           14 :          radius = 10.0_dp**(-i)
     240           42 :          CALL hall_response([-radius, radius], j2, 0.0_dp, 0.2_dp, 0.3_dp, hall, status)
     241           14 :          analytic = 2.0_dp*TANH(radius/0.4_dp)/(4*radius**2 + 0.3_dp**2)
     242           16 :          IF (status /= 0 .OR. ABS(hall(1, 2)/analytic - 1.0_dp) > 1.e-12_dp) THEN
     243            0 :             ERROR STOP 'Near-degenerate Hall limit'
     244              :          END IF
     245              :       END DO
     246              : 
     247            2 :       energies = [-0.9_dp, -0.9_dp, 0.8_dp, 0.8_dp]
     248            8 :       DO a = 1, 3
     249           30 :          DO j = 1, 4
     250          126 :             DO i = 1, 4
     251          120 :                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          248 :          current(:, :, a) = current(:, :, a) - CONJG(TRANSPOSE(current(:, :, a)))
     255              :       END DO
     256            2 :       CALL hall_response(energies, current, 0.1_dp, 0.15_dp, 0.07_dp, hall, status)
     257           26 :       IF (status /= 0 .OR. MAXVAL(ABS(hall)) < 0.1_dp) ERROR STOP 'Nontrivial four-band response'
     258            2 :       CALL liouville_reference(energies, current, 0.1_dp, 0.15_dp, 0.07_dp, reference)
     259           26 :       error = MAXVAL(ABS(hall - 0.5_dp*(reference - TRANSPOSE(reference))))
     260            2 :       IF (error > 2.e-12_dp) ERROR STOP 'Full Liouvillian reference mismatch'
     261            2 :       PRINT '(A,ES14.6)', 'Hall Liouvillian residual: ', error
     262            8 :       DO a = 1, 3
     263            6 :          unit = 0.0_dp
     264            6 :          unit(a) = 1.0_dp
     265           98 :          IF (DOT_PRODUCT(unit, MATMUL(reference, unit)) < -1.e-12_dp) ERROR STOP 'Dissipation sign'
     266              :       END DO
     267              : 
     268            2 :       u = 0.0_dp
     269           14 :       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           14 :                              (0.6_dp, 0.0_dp)], [2, 2])
     272            8 :       DO a = 1, 3
     273         1136 :          changed(:, :, a) = MATMUL(CONJG(TRANSPOSE(u)), MATMUL(current(:, :, a), u))
     274              :       END DO
     275            2 :       CALL hall_response(energies, changed, 0.1_dp, 0.15_dp, 0.07_dp, check, status)
     276           26 :       IF (status /= 0 .OR. MAXVAL(ABS(hall - check)) > 2.e-12_dp) ERROR STOP 'Degenerate gauge covariance'
     277           10 :       CALL hall_response(energies + 2.3_dp, current, 2.4_dp, 0.15_dp, 0.07_dp, check, status)
     278           26 :       IF (status /= 0 .OR. MAXVAL(ABS(hall - check)) > 2.e-12_dp) ERROR STOP 'Energy zero covariance'
     279          128 :       changed = current([3, 1, 4, 2], [3, 1, 4, 2], :)
     280           10 :       CALL hall_response(energies([3, 1, 4, 2]), changed, 0.1_dp, 0.15_dp, 0.07_dp, check, status)
     281           26 :       IF (status /= 0 .OR. MAXVAL(ABS(hall - check)) > 2.e-12_dp) ERROR STOP 'Band permutation covariance'
     282          128 :       CALL hall_response(energies, CONJG(current), 0.1_dp, 0.15_dp, 0.07_dp, check, status)
     283           26 :       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            2 :                           0.0_dp, 0.0_dp, -1.0_dp], [3, 3])
     286            2 :       changed = 0.0_dp
     287            8 :       DO a = 1, 3
     288           26 :          DO b = 1, 3
     289          384 :             changed(:, :, a) = changed(:, :, a) + rotation(a, b)*current(:, :, b)
     290              :          END DO
     291              :       END DO
     292            2 :       CALL hall_response(energies, changed, 0.1_dp, 0.15_dp, 0.07_dp, check, status)
     293          158 :       reference = MATMUL(rotation, MATMUL(hall, TRANSPOSE(rotation)))
     294           26 :       IF (status /= 0 .OR. MAXVAL(ABS(reference - check)) > 2.e-12_dp) ERROR STOP 'Cartesian tensor covariance'
     295              : 
     296            2 :       CALL hall_response(energies, current, 0.0_dp, 0.0_dp, 0.1_dp, check, status)
     297            2 :       IF (status == 0) ERROR STOP 'Zero temperature accepted'
     298            2 :       CALL hall_response(energies, current, 0.0_dp, 0.1_dp, 0.0_dp, check, status)
     299            2 :       IF (status == 0) ERROR STOP 'Zero relaxation accepted'
     300            2 :       CALL hall_response(energies, current, ieee_value(0.0_dp, ieee_quiet_nan), 0.1_dp, 0.1_dp, check, status)
     301            2 :       IF (status == 0) ERROR STOP 'NaN chemical potential accepted'
     302            2 :       changed = current
     303            2 :       changed(1, 2, 1) = changed(1, 2, 1) + 0.1_dp
     304            2 :       CALL hall_response(energies, changed, 0.0_dp, 0.1_dp, 0.1_dp, check, status)
     305            2 :       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            2 :       masses = [-1.0_dp, 1.0_dp, 3.0_dp]
     310            2 :       expected = [-1.0_dp, 1.0_dp, 0.0_dp]
     311            2 :       s2 = 0.0_dp
     312            2 :       s2(1, 1) = 1.0_dp
     313            2 :       s2(2, 2) = 1.0_dp
     314            2 :       ds2 = 0.0_dp
     315            2 :       d2 = 0.0_dp
     316            2 :       gamma = 1.e-3_dp
     317            8 :       DO imass = 1, 3
     318            6 :          mass = masses(imass)
     319            6 :          integrated = 0.0_dp
     320            6 :          curvature = 0.0_dp
     321            6 :          error = 0.0_dp
     322            6 :          gauge_error = 0.0_dp
     323          294 :          DO iy = 0, mesh - 1
     324          288 :             ky = 2*pi*REAL(iy, dp)/mesh
     325        14118 :             DO ix = 0, mesh - 1
     326        13824 :                kx = 2*pi*REAL(ix, dp)/mesh
     327        13824 :                h2(1, 1) = mass + COS(kx) + COS(ky)
     328        13824 :                h2(2, 2) = -h2(1, 1)
     329        13824 :                h2(1, 2) = SIN(kx) - iu*SIN(ky)
     330        13824 :                h2(2, 1) = CONJG(h2(1, 2))
     331        13824 :                dh2 = 0.0_dp
     332        13824 :                dh2(1, 1, 1) = -SIN(kx)
     333        13824 :                dh2(2, 2, 1) = SIN(kx)
     334        13824 :                dh2(1, 2, 1) = COS(kx)
     335        13824 :                dh2(2, 1, 1) = COS(kx)
     336        13824 :                dh2(1, 1, 2) = -SIN(ky)
     337        13824 :                dh2(2, 2, 2) = SIN(ky)
     338        13824 :                dh2(1, 2, 2) = -iu*COS(ky)
     339        13824 :                dh2(2, 1, 2) = iu*COS(ky)
     340        13824 :                CALL bloch_current(h2, s2, dh2, ds2, d2, e2, j2, status)
     341        13824 :                IF (status /= 0) ERROR STOP 'Chern-model Bloch current'
     342        13824 :                CALL hall_response(e2, j2, 0.0_dp, 0.01_dp, gamma, check, status)
     343        13824 :                IF (status /= 0) ERROR STOP 'Chern-model Hall kernel'
     344        13824 :                radius = SQRT(SIN(kx)**2 + SIN(ky)**2 + (mass + COS(kx) + COS(ky))**2)
     345        13824 :                omega = (COS(kx) + COS(ky) + mass*COS(kx)*COS(ky))/(2*radius**3)
     346        13824 :                analytic = -omega*4*radius**2/(4*radius**2 + gamma**2)*TANH(radius/0.02_dp)
     347        13824 :                error = MAX(error, ABS(check(1, 2) - analytic))
     348        13824 :                CALL ao_gauge_reference(kx, ky, h2, dh2, gamma, reference)
     349       179712 :                gauge_error = MAX(gauge_error, MAXVAL(ABS(check - reference)))
     350        13824 :                integrated = integrated + check(1, 2)*2*pi/mesh**2
     351        14112 :                curvature = curvature + omega*2*pi/mesh**2
     352              :             END DO
     353              :          END DO
     354            6 :          IF (error > 1.e-12_dp) ERROR STOP 'Analytic Berry-curvature response mismatch'
     355            6 :          IF (ABS(integrated - expected(imass)) > 1.e-6_dp) ERROR STOP 'Chern Hall limit'
     356            6 :          IF (ABS(curvature + expected(imass)) > 1.e-10_dp) ERROR STOP 'Independent Chern integral'
     357            6 :          IF (gauge_error > 1.e-12_dp) ERROR STOP 'Nonorthogonal AO Hall covariance'
     358            6 :          PRINT '(A,F5.1,3ES18.10)', 'Hall Chern model mass, sigma, Chern, residual: ', mass, integrated, curvature, error
     359            8 :          PRINT '(A,ES14.6)', 'Hall k-dependent nonorthogonal AO residual: ', gauge_error
     360              :       END DO
     361            2 :       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        13824 :       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        13824 :          basis = 0.0_dp
     385        13824 :          basis(1, 1) = 1.1_dp + 0.1_dp*COS(kx)
     386        13824 :          basis(2, 2) = 0.9_dp + 0.1_dp*SIN(ky)
     387        13824 :          basis(1, 2) = 0.13_dp*EXP(iu*(kx + ky))
     388        13824 :          basis(2, 1) = 0.07_dp*EXP(iu*(2*kx - ky))
     389        13824 :          db = 0.0_dp
     390        13824 :          db(1, 1, 1) = -0.1_dp*SIN(kx)
     391        13824 :          db(2, 2, 2) = 0.1_dp*COS(ky)
     392        41472 :          db(1, 2, 1:2) = iu*basis(1, 2)
     393        13824 :          db(2, 1, 1) = 2*iu*basis(2, 1)
     394        13824 :          db(2, 1, 2) = -iu*basis(2, 1)
     395       400896 :          hx = MATMUL(CONJG(TRANSPOSE(basis)), MATMUL(h, basis))
     396       207360 :          sx = MATMUL(CONJG(TRANSPOSE(basis)), basis)
     397        55296 :          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      4022784 :                            MATMUL(CONJG(TRANSPOSE(basis)), MATMUL(h, db(:, :, a)))
     401              :             dsx(:, :, a) = MATMUL(CONJG(TRANSPOSE(db(:, :, a))), basis) + &
     402      1700352 :                            MATMUL(CONJG(TRANSPOSE(basis)), db(:, :, a))
     403      1133568 :             dx(:, :, a) = -iu*MATMUL(CONJG(TRANSPOSE(db(:, :, a))), basis) + REAL(a, dp)*sx
     404              :          END DO
     405        13824 :          CALL bloch_current(hx, sx, dhx, dsx, dx, e, jx, status)
     406        13824 :          IF (status /= 0) ERROR STOP 'Nonorthogonal AO Chern model'
     407        13824 :          CALL hall_response(e, jx, 0.0_dp, 0.01_dp, gamma, hall, status)
     408        13824 :          IF (status /= 0) ERROR STOP 'Nonorthogonal AO Hall kernel'
     409            2 :       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            2 :       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           10 :          DO j = 1, 4
     433           42 :             DO i = 1, 4
     434           40 :                basis(i, j) = EXP(iu*2*pi*REAL((i - 1)*(j - 1), dp)/4)/2
     435              :             END DO
     436              :          END DO
     437            2 :          h = 0.0_dp
     438           10 :          DO i = 1, 4
     439           10 :             h(i, i) = e(i)
     440              :          END DO
     441          338 :          h = MATMUL(basis, MATMUL(h, CONJG(TRANSPOSE(basis))))
     442            8 :          DO a = 1, 3
     443         1254 :             v(:, :, a) = MATMUL(basis, MATMUL(iu*currents(:, :, a), CONJG(TRANSPOSE(basis))))
     444           30 :             DO j = 1, 4
     445          126 :                DO i = 1, 4
     446          120 :                   drive(i, j) = fermi_slope(e(i), e(j), mu, kt)*iu*currents(i, j, a)
     447              :                END DO
     448              :             END DO
     449         1014 :             drive = MATMUL(basis, MATMUL(drive, CONJG(TRANSPOSE(basis))))
     450            8 :             rhs(:, a) = RESHAPE(drive, [16])
     451              :          END DO
     452            2 :          liouvillian = 0.0_dp
     453           10 :          DO j = 1, 4
     454           42 :             DO i = 1, 4
     455           32 :                row = i + 4*(j - 1)
     456           32 :                liouvillian(row, row) = gamma
     457          168 :                DO l = 1, 4
     458          672 :                   DO k = 1, 4
     459          512 :                      column = k + 4*(l - 1)
     460          512 :                      IF (j == l) liouvillian(row, column) = liouvillian(row, column) + iu*h(i, k)
     461          640 :                      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            2 :          CALL zgesv(16, 3, liouvillian, 16, pivot, rhs, 16, info)
     467            2 :          IF (info /= 0) ERROR STOP 'Liouvillian solve failed'
     468            8 :          DO b = 1, 3
     469            6 :             response = RESHAPE(rhs(:, b), [4, 4])
     470           26 :             DO a = 1, 3
     471          378 :                trace = SUM(v(:, :, a)*TRANSPOSE(response))
     472           24 :                sigma(a, b) = -REAL(trace, dp)
     473              :             END DO
     474              :          END DO
     475            2 :       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              : 
     481              : END MODULE kubo_test_cases
     482              : 
     483            2 : PROGRAM kubo_unittest
     484            2 :    USE kubo_test_cases, ONLY: check_projected_current, check_bloch_current, check_hall_response
     485              :    IMPLICIT NONE
     486            2 :    CALL check_projected_current()
     487            2 :    CALL check_bloch_current()
     488            2 :    CALL check_hall_response()
     489            2 : END PROGRAM kubo_unittest
        

Generated by: LCOV version 2.0-1