LCOV - code coverage report
Current view: top level - src - smearing_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:561f475) Lines: 54.9 % 397 218
Test Date: 2026-06-21 06:48:54 Functions: 66.7 % 9 6

            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              : 
       8              : ! **************************************************************************************************
       9              : !> \brief Unified smearing module supporting four methods:
      10              : !>          smear_fermi_dirac  — Fermi-Dirac distribution
      11              : !>          smear_gaussian     — Gaussian broadening
      12              : !>          smear_mp           — Methfessel-Paxton first order
      13              : !>          smear_mv           — Marzari-Vanderbilt (cold smearing)
      14              : !>
      15              : !>        All methods share the bisection framework, LFOMO/HOMO logic, and the
      16              : !>        analytical rank-1 Jacobian.  Only the per-state math (f, kTS, g_i)
      17              : !>        differs, selected via a method integer from input_constants.
      18              : !>
      19              : !> \par History
      20              : !>      09.2008: Created (fermi_utils.F)
      21              : !>      02.2026: Extended to more smearing method and renamed
      22              : !> \author Joost VandeVondele
      23              : ! **************************************************************************************************
      24              : MODULE smearing_utils
      25              : 
      26              :    USE bibliography,                    ONLY: FuHo1983,&
      27              :                                               Marzari1999,&
      28              :                                               Mermin1965,&
      29              :                                               MethfesselPaxton1989,&
      30              :                                               cite_reference,&
      31              :                                               dosSantos2023
      32              :    USE input_constants,                 ONLY: smear_fermi_dirac,&
      33              :                                               smear_gaussian,&
      34              :                                               smear_mp,&
      35              :                                               smear_mv
      36              :    USE kahan_sum,                       ONLY: accurate_sum
      37              :    USE kinds,                           ONLY: dp
      38              :    USE mathconstants,                   ONLY: rootpi,&
      39              :                                               sqrt2,&
      40              :                                               sqrthalf
      41              : #include "base/base_uses.f90"
      42              : 
      43              :    IMPLICIT NONE
      44              : 
      45              :    PRIVATE
      46              : 
      47              :    ! Unified interface (method as parameter)
      48              :    PUBLIC :: SmearOcc, SmearFixed, SmearFixedDeriv, SmearFixedDerivMV
      49              :    PUBLIC :: Smearkp, Smearkp2
      50              :    PRIVATE :: cite_smearing
      51              : 
      52              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'smearing_utils'
      53              :    INTEGER, PARAMETER, PRIVATE                           :: BISECT_MAX_ITER = 400
      54              :    INTEGER, PARAMETER, PRIVATE                           :: NEWTON_MAX_ITER = 50
      55              :    INTEGER, PARAMETER, PRIVATE                           :: NEWTON_MAX_BACKTRACK = 20
      56              :    REAL(KIND=dp), PARAMETER, PRIVATE                     :: MPMV_MAX_NEWTON_STEP = 2.0_dp
      57              : 
      58              : CONTAINS
      59              : ! **************************************************************************************************
      60              : !> \brief   Citation of Smearing methods
      61              : !> \param method ...
      62              : ! **************************************************************************************************
      63        55934 :    SUBROUTINE cite_smearing(method)
      64              :       INTEGER, INTENT(IN)                                :: method
      65              : 
      66        89820 :       SELECT CASE (method)
      67              :       CASE (smear_fermi_dirac)
      68        33886 :          CALL cite_reference(Mermin1965)
      69              :       CASE (smear_gaussian)
      70        21992 :          CALL cite_reference(FuHo1983)
      71              :       CASE (smear_mp)
      72           28 :          CALL cite_reference(FuHo1983)
      73           28 :          CALL cite_reference(MethfesselPaxton1989)
      74           28 :          CALL cite_reference(dosSantos2023)
      75              :       CASE (smear_mv)
      76           28 :          CALL cite_reference(FuHo1983)
      77           28 :          CALL cite_reference(Marzari1999)
      78        55962 :          CALL cite_reference(dosSantos2023)
      79              :       END SELECT
      80        55934 :    END SUBROUTINE cite_smearing
      81              : 
      82              : ! **************************************************************************************************
      83              : !> \brief   Returns occupations and smearing correction for a given set of
      84              : !>          energies and chemical potential, using one of four smearing methods.
      85              : !>
      86              : !>          Fermi-Dirac:  f_i = occ / [1 + exp((e_i - mu)/sigma)]
      87              : !>          Gaussian:     f_i = (occ/2) * erfc[(e_i - mu)/sigma]
      88              : !>          MP-1:         f_i = (occ/2) * erfc(x) - occ*x/(2*sqrt(pi)) * exp(-x^2)
      89              : !>          MV:           f_i = (occ/2) * erfc(u) + occ/(sqrt(2*pi)) * exp(-u^2),  u = x + 1/sqrt(2)
      90              : !>
      91              : !>          kTS is the smearing correction to the free energy (physically -TS
      92              : !>          for Fermi-Dirac; a variational correction term for the other methods).
      93              : !>          It enters the total energy and the Gillan extrapolation E(0) = E - kTS/2.
      94              : !>
      95              : !> \param f       occupations (output)
      96              : !> \param N       total number of electrons (output)
      97              : !> \param kTS     smearing correction to the free energy (output)
      98              : !> \param e       eigenvalues (input)
      99              : !> \param mu      chemical potential (input)
     100              : !> \param sigma   smearing width: kT for Fermi-Dirac, sigma for others (input)
     101              : !> \param maxocc  maximum occupation of an orbital (input)
     102              : !> \param method  smearing method selector from input_constants (input)
     103              : !> \param estate  excited state index for core-level spectroscopy (optional)
     104              : !> \param festate occupation of the excited state (optional)
     105              : ! **************************************************************************************************
     106      1435034 :    SUBROUTINE SmearOcc(f, N, kTS, e, mu, sigma, maxocc, method, estate, festate)
     107              : 
     108              :       REAL(KIND=dp), INTENT(OUT)                         :: f(:), N, kTS
     109              :       REAL(KIND=dp), INTENT(IN)                          :: e(:), mu, sigma, maxocc
     110              :       INTEGER, INTENT(IN)                                :: method
     111              :       INTEGER, INTENT(IN), OPTIONAL                      :: estate
     112              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: festate
     113              : 
     114              :       INTEGER                                            :: i, Nstate
     115              :       REAL(KIND=dp)                                      :: arg, expu2, expx2, occupation, term1, &
     116              :                                                             term2, tmp, tmp2, tmp3, tmp4, tmplog, &
     117              :                                                             u, x
     118              : 
     119      1435034 :       Nstate = SIZE(e)
     120      1435034 :       kTS = 0.0_dp
     121              : 
     122     49495306 :       DO i = 1, Nstate
     123     48060272 :          IF (PRESENT(estate) .AND. PRESENT(festate)) THEN
     124     48026208 :             IF (i == estate) THEN
     125         4012 :                occupation = festate
     126              :             ELSE
     127     48022196 :                occupation = maxocc
     128              :             END IF
     129              :          ELSE
     130        34064 :             occupation = maxocc
     131              :          END IF
     132              : 
     133      1435034 :          SELECT CASE (method)
     134              :          CASE (smear_fermi_dirac)
     135     45796896 :             IF (e(i) > mu) THEN
     136     30079643 :                arg = -(e(i) - mu)/sigma
     137     30079643 :                tmp = EXP(arg)
     138     30079643 :                tmp4 = tmp + 1.0_dp
     139     30079643 :                tmp2 = tmp/tmp4
     140     30079643 :                tmp3 = 1.0_dp/tmp4
     141     30079643 :                tmplog = -LOG(tmp4)
     142     30079643 :                term1 = tmp2*(arg + tmplog)
     143     30079643 :                term2 = tmp3*tmplog
     144              :             ELSE
     145     15717253 :                arg = (e(i) - mu)/sigma
     146     15717253 :                tmp = EXP(arg)
     147     15717253 :                tmp4 = tmp + 1.0_dp
     148     15717253 :                tmp2 = 1.0_dp/tmp4
     149     15717253 :                tmp3 = tmp/tmp4
     150     15717253 :                tmplog = -LOG(tmp4)
     151     15717253 :                term1 = tmp2*tmplog
     152     15717253 :                term2 = tmp3*(arg + tmplog)
     153              :             END IF
     154     45796896 :             f(i) = occupation*tmp2
     155     45796896 :             kTS = kTS + sigma*occupation*(term1 + term2)
     156              : 
     157              :          CASE (smear_gaussian)
     158      2263376 :             x = (e(i) - mu)/sigma
     159      2263376 :             expx2 = EXP(-x*x)
     160      2263376 :             f(i) = occupation*0.5_dp*ERFC(x)
     161      2263376 :             kTS = kTS - (sigma/(2.0_dp*rootpi))*occupation*expx2
     162              : 
     163              :          CASE (smear_mp)
     164            0 :             x = (e(i) - mu)/sigma
     165            0 :             expx2 = EXP(-x*x)
     166            0 :             f(i) = occupation*(0.5_dp*ERFC(x) - x/(2.0_dp*rootpi)*expx2)
     167            0 :             kTS = kTS + (sigma/(4.0_dp*rootpi))*occupation*(2.0_dp*x*x - 1.0_dp)*expx2
     168              : 
     169              :          CASE (smear_mv)
     170            0 :             x = (e(i) - mu)/sigma
     171            0 :             u = x + sqrthalf
     172            0 :             expu2 = EXP(-u*u)
     173            0 :             f(i) = occupation*(0.5_dp*ERFC(u) + expu2/(sqrt2*rootpi))
     174            0 :             kTS = kTS - (sigma/(sqrt2*rootpi))*occupation*u*expu2
     175              : 
     176              :          CASE DEFAULT
     177     48060272 :             CPABORT("SmearOcc: unknown smearing method")
     178              :          END SELECT
     179              :       END DO
     180              : 
     181      1435034 :       N = accurate_sum(f)
     182              : 
     183      1435034 :    END SUBROUTINE SmearOcc
     184              : 
     185              : ! **************************************************************************************************
     186              : !> \brief   k-point version of SmearOcc (module-private).
     187              : !>          Computes occupations and kTS for a 2D array of eigenvalues
     188              : !>          (nmo x nkp) weighted by k-point weights.
     189              : !>          Falls back to a step function when sigma < 1e-14.
     190              : !>
     191              : !> \param f       occupations (nmo x nkp, output)
     192              : !> \param nel     total number of electrons (output)
     193              : !> \param kTS     smearing correction (output)
     194              : !> \param e       eigenvalues (nmo x nkp, input)
     195              : !> \param mu      chemical potential (input)
     196              : !> \param wk      k-point weights (input)
     197              : !> \param sigma   smearing width (input)
     198              : !> \param maxocc  maximum occupation (input)
     199              : !> \param method  smearing method selector (input)
     200              : ! **************************************************************************************************
     201       126892 :    SUBROUTINE Smear2(f, nel, kTS, e, mu, wk, sigma, maxocc, method)
     202              : 
     203              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: f
     204              :       REAL(KIND=dp), INTENT(OUT)                         :: nel, kTS
     205              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: e
     206              :       REAL(KIND=dp), INTENT(IN)                          :: mu
     207              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: wk
     208              :       REAL(KIND=dp), INTENT(IN)                          :: sigma, maxocc
     209              :       INTEGER, INTENT(IN)                                :: method
     210              : 
     211              :       INTEGER                                            :: ik, is, nkp, nmo
     212              :       REAL(KIND=dp)                                      :: arg, expu2, expx2, term1, term2, tmp, &
     213              :                                                             tmp2, tmp3, tmp4, tmplog, u, x
     214              : 
     215       126892 :       nmo = SIZE(e, 1)
     216       126892 :       nkp = SIZE(e, 2)
     217       126892 :       kTS = 0.0_dp
     218              : 
     219       126892 :       IF (sigma > 1.0e-14_dp) THEN
     220       498154 :          DO ik = 1, nkp
     221     18189012 :             DO is = 1, nmo
     222       432898 :                SELECT CASE (method)
     223              :                CASE (smear_fermi_dirac)
     224     16709424 :                   IF (e(is, ik) > mu) THEN
     225      9969818 :                      arg = -(e(is, ik) - mu)/sigma
     226      9969818 :                      tmp = EXP(arg)
     227      9969818 :                      tmp4 = tmp + 1.0_dp
     228      9969818 :                      tmp2 = tmp/tmp4
     229      9969818 :                      tmp3 = 1.0_dp/tmp4
     230      9969818 :                      tmplog = -LOG(tmp4)
     231      9969818 :                      term1 = tmp2*(arg + tmplog)
     232      9969818 :                      term2 = tmp3*tmplog
     233              :                   ELSE
     234      6739606 :                      arg = (e(is, ik) - mu)/sigma
     235      6739606 :                      tmp = EXP(arg)
     236      6739606 :                      tmp4 = tmp + 1.0_dp
     237      6739606 :                      tmp2 = 1.0_dp/tmp4
     238      6739606 :                      tmp3 = tmp/tmp4
     239      6739606 :                      tmplog = -LOG(tmp4)
     240      6739606 :                      term1 = tmp2*tmplog
     241      6739606 :                      term2 = tmp3*(arg + tmplog)
     242              :                   END IF
     243     16709424 :                   f(is, ik) = maxocc*tmp2
     244     16709424 :                   kTS = kTS + sigma*maxocc*(term1 + term2)*wk(ik)
     245              : 
     246              :                CASE (smear_gaussian)
     247       882874 :                   x = (e(is, ik) - mu)/sigma
     248       882874 :                   expx2 = EXP(-x*x)
     249       882874 :                   f(is, ik) = maxocc*0.5_dp*ERFC(x)
     250       882874 :                   kTS = kTS - (sigma/(2.0_dp*rootpi))*maxocc*expx2*wk(ik)
     251              : 
     252              :                CASE (smear_mp)
     253        44800 :                   x = (e(is, ik) - mu)/sigma
     254        44800 :                   expx2 = EXP(-x*x)
     255        44800 :                   f(is, ik) = maxocc*(0.5_dp*ERFC(x) - x/(2.0_dp*rootpi)*expx2)
     256        44800 :                   kTS = kTS + (sigma/(4.0_dp*rootpi))*maxocc*(2.0_dp*x*x - 1.0_dp)*expx2*wk(ik)
     257              : 
     258              :                CASE (smear_mv)
     259        53760 :                   x = (e(is, ik) - mu)/sigma
     260        53760 :                   u = x + sqrthalf
     261        53760 :                   expu2 = EXP(-u*u)
     262        53760 :                   f(is, ik) = maxocc*(0.5_dp*ERFC(u) + expu2/(sqrt2*rootpi))
     263        53760 :                   kTS = kTS - (sigma/(sqrt2*rootpi))*maxocc*u*expu2*wk(ik)
     264              : 
     265              :                CASE DEFAULT
     266     17690858 :                   CPABORT("Smear2: unknown smearing method")
     267              :                END SELECT
     268              :             END DO
     269              :          END DO
     270              :       ELSE
     271              :          ! Zero-width limit: step function
     272       259866 :          DO ik = 1, nkp
     273      2229496 :             DO is = 1, nmo
     274      2167860 :                IF (e(is, ik) <= mu) THEN
     275      1342802 :                   f(is, ik) = maxocc
     276              :                ELSE
     277       626828 :                   f(is, ik) = 0.0_dp
     278              :                END IF
     279              :             END DO
     280              :          END DO
     281              :       END IF
     282              : 
     283       126892 :       nel = 0.0_dp
     284       758020 :       DO ik = 1, nkp
     285       758020 :          nel = nel + accurate_sum(f(1:nmo, ik))*wk(ik)
     286              :       END DO
     287              : 
     288       126892 :    END SUBROUTINE Smear2
     289              : 
     290              : ! **************************************************************************************************
     291              : !> \brief   Bisection search for the chemical potential mu such that the total
     292              : !>          electron count equals N, for a given smearing method (Gamma point).
     293              : !>          Brackets mu by expanding outward from [min(e), max(e)] in steps
     294              : !>          of sigma, then bisects to machine precision.
     295              : !>
     296              : !>          For MP-1 and MV: the occupation function is non-monotonic, so it's
     297              : !>          possible that pure bisection find a spurious root.
     298              : !>          We first bisect with Gaussian smearing to get a reliable initial mu,
     299              : !>          then refine with Newton's method using the actual method's dN/dmu.
     300              : !>          (dos Santos & Marzari, PRB 2023)
     301              : !>
     302              : !> \param f       occupations (output)
     303              : !> \param mu      chemical potential found by bisection (output)
     304              : !> \param kTS     smearing correction (output)
     305              : !> \param e       eigenvalues (input)
     306              : !> \param N       target number of electrons (input)
     307              : !> \param sigma   smearing width (input)
     308              : !> \param maxocc  maximum occupation (input)
     309              : !> \param method  smearing method selector (input)
     310              : !> \param estate  excited state index for core-level spectroscopy (optional)
     311              : !> \param festate occupation of the excited state (optional)
     312              : ! **************************************************************************************************
     313        25532 :    SUBROUTINE SmearFixed(f, mu, kTS, e, N, sigma, maxocc, method, estate, festate)
     314              : 
     315              :       REAL(KIND=dp), INTENT(OUT)                         :: f(:), mu, kTS
     316              :       REAL(KIND=dp), INTENT(IN)                          :: e(:), N, sigma, maxocc
     317              :       INTEGER, INTENT(IN)                                :: method
     318              :       INTEGER, INTENT(IN), OPTIONAL                      :: estate
     319              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: festate
     320              : 
     321              :       INTEGER                                            :: iback, iter, my_estate, Nstate
     322              :       REAL(KIND=dp) :: Gsum, mu_best, mu_max, mu_min, mu_now, mu_trial, my_festate, N_now, N_tmp, &
     323              :          N_trial, res_best, res_now, res_trial, step, step_try
     324        25532 :       REAL(KIND=dp), ALLOCATABLE                         :: gvec(:)
     325              : 
     326        25532 :       IF (PRESENT(estate) .AND. PRESENT(festate)) THEN
     327        25492 :          my_estate = estate
     328        25492 :          my_festate = festate
     329              :       ELSE
     330           40 :          my_estate = NINT(maxocc)
     331           40 :          my_festate = my_estate
     332              :       END IF
     333              : 
     334        25532 :       Nstate = SIZE(e)
     335              : 
     336        25532 :       CALL cite_smearing(method)
     337              : 
     338        25532 :       SELECT CASE (method)
     339              : 
     340              :          ! Non-monotonic methods: Gaussian bisection + Newton refinement
     341              :       CASE (smear_mp, smear_mv)
     342              :          ! Step 1: Gaussian bisection for a reliable initial mu
     343            0 :          mu_min = MINVAL(e)
     344            0 :          iter = 0
     345            0 :          DO
     346            0 :             iter = iter + 1
     347            0 :             CALL SmearOcc(f, N_tmp, kTS, e, mu_min, sigma, maxocc, smear_gaussian, my_estate, my_festate)
     348            0 :             IF (N_tmp <= N) EXIT
     349            0 :             IF (iter > 20) THEN
     350            0 :                CPABORT("SmearFixed: failed to bracket lower chemical potential")
     351              :             END IF
     352            0 :             mu_min = mu_min - sigma
     353              :          END DO
     354              : 
     355            0 :          mu_max = MAXVAL(e)
     356            0 :          iter = 0
     357            0 :          DO
     358            0 :             iter = iter + 1
     359            0 :             CALL SmearOcc(f, N_tmp, kTS, e, mu_max, sigma, maxocc, smear_gaussian, my_estate, my_festate)
     360            0 :             IF (N_tmp >= N) EXIT
     361            0 :             IF (iter > 20) THEN
     362            0 :                CPABORT("SmearFixed: failed to bracket upper chemical potential")
     363              :             END IF
     364            0 :             mu_max = mu_max + sigma
     365              :          END DO
     366              : 
     367              :          iter = 0
     368            0 :          DO WHILE (mu_max - mu_min > EPSILON(mu)*MAX(1.0_dp, ABS(mu_max), ABS(mu_min)))
     369            0 :             iter = iter + 1
     370            0 :             mu_now = (mu_max + mu_min)/2.0_dp
     371            0 :             CALL SmearOcc(f, N_now, kTS, e, mu_now, sigma, maxocc, smear_gaussian, my_estate, my_festate)
     372            0 :             IF (N_now <= N) THEN
     373            0 :                mu_min = mu_now
     374              :             ELSE
     375            0 :                mu_max = mu_now
     376              :             END IF
     377            0 :             IF (iter > BISECT_MAX_ITER) EXIT
     378              :          END DO
     379            0 :          mu = (mu_max + mu_min)/2.0_dp
     380              : 
     381              :          ! Step 2: damped Newton refinement with the actual method.  MP/MV
     382              :          ! occupations are not monotonic functions of mu, therefore an
     383              :          ! unrestricted Newton step can jump to a remote root or increase the
     384              :          ! electron-count residual.  Keep the root closest to the Gaussian
     385              :          ! solution by accepting only residual-reducing, size-limited steps.
     386            0 :          ALLOCATE (gvec(Nstate))
     387            0 :          CALL SmearOcc(f, N_now, kTS, e, mu, sigma, maxocc, method, my_estate, my_festate)
     388            0 :          res_best = ABS(N_now - N)
     389            0 :          mu_best = mu
     390            0 :          DO iter = 1, NEWTON_MAX_ITER
     391            0 :             res_now = ABS(N_now - N)
     392            0 :             IF (res_now < N*1.0e-12_dp) EXIT
     393            0 :             CALL compute_gvec(gvec, f, e, mu, sigma, maxocc, Nstate, method, my_estate, my_festate)
     394            0 :             Gsum = accurate_sum(gvec)
     395            0 :             IF (ABS(Gsum) < EPSILON(Gsum)) EXIT
     396              : 
     397            0 :             step = (N - N_now)/Gsum
     398            0 :             step = SIGN(MIN(ABS(step), MPMV_MAX_NEWTON_STEP*sigma), step)
     399            0 :             step_try = step
     400            0 :             DO iback = 1, NEWTON_MAX_BACKTRACK
     401            0 :                mu_trial = mu + step_try
     402              :                CALL SmearOcc(f, N_trial, kTS, e, mu_trial, sigma, maxocc, method, &
     403            0 :                              my_estate, my_festate)
     404            0 :                res_trial = ABS(N_trial - N)
     405            0 :                IF (res_trial < res_now) THEN
     406            0 :                   mu = mu_trial
     407            0 :                   N_now = N_trial
     408            0 :                   IF (res_trial < res_best) THEN
     409            0 :                      res_best = res_trial
     410            0 :                      mu_best = mu
     411              :                   END IF
     412              :                   EXIT
     413              :                END IF
     414            0 :                step_try = 0.5_dp*step_try
     415              :             END DO
     416            0 :             IF (iback > NEWTON_MAX_BACKTRACK) EXIT
     417              :          END DO
     418            0 :          DEALLOCATE (gvec)
     419            0 :          mu = mu_best
     420              : 
     421              :          ! Final evaluation
     422            0 :          CALL SmearOcc(f, N_now, kTS, e, mu, sigma, maxocc, method, my_estate, my_festate)
     423            0 :          IF (ABS(N_now - N) >= N*1.0e-12_dp) THEN
     424            0 :             CPWARN("SmearFixed: MP/MV smearing did not reach the requested electron count")
     425              :          END IF
     426              : 
     427              :          ! Monotonic methods (FD, Gaussian): pure bisection
     428              :       CASE DEFAULT
     429       875038 :          mu_min = MINVAL(e)
     430        25532 :          iter = 0
     431            0 :          DO
     432        25532 :             iter = iter + 1
     433        25532 :             CALL SmearOcc(f, N_tmp, kTS, e, mu_min, sigma, maxocc, method, my_estate, my_festate)
     434        25532 :             IF (N_tmp <= N) EXIT
     435            0 :             IF (iter > 20) THEN
     436            0 :                CPABORT("SmearFixed: failed to bracket lower chemical potential")
     437              :             END IF
     438            0 :             mu_min = mu_min - sigma
     439              :          END DO
     440              : 
     441       875038 :          mu_max = MAXVAL(e)
     442        25532 :          iter = 0
     443            0 :          DO
     444        25532 :             iter = iter + 1
     445        25532 :             CALL SmearOcc(f, N_tmp, kTS, e, mu_max, sigma, maxocc, method, my_estate, my_festate)
     446        25532 :             IF (N_tmp >= N) EXIT
     447            0 :             IF (iter > 20) THEN
     448            0 :                CPABORT("SmearFixed: failed to bracket upper chemical potential")
     449              :             END IF
     450            0 :             mu_max = mu_max + sigma
     451              :          END DO
     452              : 
     453              :          iter = 0
     454      1383594 :          DO WHILE (mu_max - mu_min > EPSILON(mu)*MAX(1.0_dp, ABS(mu_max), ABS(mu_min)))
     455      1358062 :             iter = iter + 1
     456      1358062 :             mu_now = (mu_max + mu_min)/2.0_dp
     457      1358062 :             CALL SmearOcc(f, N_now, kTS, e, mu_now, sigma, maxocc, method, my_estate, my_festate)
     458      1358062 :             IF (N_now <= N) THEN
     459       656749 :                mu_min = mu_now
     460              :             ELSE
     461       701313 :                mu_max = mu_now
     462              :             END IF
     463      1383594 :             IF (iter > BISECT_MAX_ITER) THEN
     464            0 :                CPWARN("SmearFixed: maximum bisection iterations reached")
     465            0 :                EXIT
     466              :             END IF
     467              :          END DO
     468              : 
     469        25532 :          mu = (mu_max + mu_min)/2.0_dp
     470        51064 :          CALL SmearOcc(f, N_now, kTS, e, mu, sigma, maxocc, method, my_estate, my_festate)
     471              : 
     472              :       END SELECT
     473              : 
     474        25532 :    END SUBROUTINE SmearFixed
     475              : 
     476              : ! **************************************************************************************************
     477              : !> \brief   Bisection search for mu given a target electron count (k-point case,
     478              : !>          single spin channel or spin-degenerate).
     479              : !>          Initial bracket width is max(10*sigma, 0.5) for Gaussian/MP/MV,
     480              : !>          or sigma*ln[(1-eps)/eps] for Fermi-Dirac, reflecting the different
     481              : !>          tail decay rates.
     482              : !>
     483              : !>          For MP-1 and MV: Gaussian bisection + Newton refinement
     484              : !>          (dos Santos & Marzari, PRB 2023).
     485              : !>
     486              : !> \param f       occupations (nmo x nkp, output)
     487              : !> \param mu      chemical potential (output)
     488              : !> \param kTS     smearing correction (output)
     489              : !> \param e       eigenvalues (nmo x nkp, input)
     490              : !> \param nel     target number of electrons (input)
     491              : !> \param wk      k-point weights (input)
     492              : !> \param sigma   smearing width (input)
     493              : !> \param maxocc  maximum occupation (input)
     494              : !> \param method  smearing method selector (input)
     495              : ! **************************************************************************************************
     496        30370 :    SUBROUTINE Smearkp(f, mu, kTS, e, nel, wk, sigma, maxocc, method)
     497              : 
     498              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: f
     499              :       REAL(KIND=dp), INTENT(OUT)                         :: mu, kTS
     500              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: e
     501              :       REAL(KIND=dp), INTENT(IN)                          :: nel
     502              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: wk
     503              :       REAL(KIND=dp), INTENT(IN)                          :: sigma, maxocc
     504              :       INTEGER, INTENT(IN)                                :: method
     505              : 
     506              :       REAL(KIND=dp), PARAMETER                           :: epsocc = 1.0e-12_dp
     507              : 
     508              :       INTEGER                                            :: bisect_method, iback, ik, is, iter, nkp, &
     509              :                                                             nmo
     510              :       REAL(KIND=dp)                                      :: de, dNdmu, expu2, expx2, mu_best, &
     511              :                                                             mu_max, mu_min, N_now, N_trial, &
     512              :                                                             res_best, res_now, res_trial, step, &
     513              :                                                             step_try, u, x
     514              : 
     515        30370 :       nmo = SIZE(e, 1)
     516        30370 :       nkp = SIZE(e, 2)
     517              : 
     518        30370 :       CALL cite_smearing(method)
     519              : 
     520              :       ! Choose bisection method: Gaussian for MP/MV, actual method for FD/Gaussian
     521        30426 :       SELECT CASE (method)
     522              :       CASE (smear_mp, smear_mv)
     523           56 :          bisect_method = smear_gaussian
     524              :       CASE DEFAULT
     525        30370 :          bisect_method = method
     526              :       END SELECT
     527              : 
     528              :       ! Initial bracket
     529         9904 :       SELECT CASE (bisect_method)
     530              :       CASE (smear_fermi_dirac)
     531         9904 :          de = sigma*LOG((1.0_dp - epsocc)/epsocc)
     532              :       CASE DEFAULT
     533        30370 :          de = 10.0_dp*sigma
     534              :       END SELECT
     535        30370 :       de = MAX(de, 0.5_dp)
     536              : 
     537              :       ! Bisection with bisect_method
     538      2214112 :       mu_min = MINVAL(e) - de
     539      2214112 :       mu_max = MAXVAL(e) + de
     540        30370 :       iter = 0
     541        94134 :       DO WHILE (mu_max - mu_min > EPSILON(mu)*MAX(1.0_dp, ABS(mu_max), ABS(mu_min)))
     542        94134 :          iter = iter + 1
     543        94134 :          mu = (mu_max + mu_min)/2.0_dp
     544        94134 :          CALL Smear2(f, N_now, kTS, e, mu, wk, sigma, maxocc, bisect_method)
     545              : 
     546        94134 :          IF (ABS(N_now - nel) < nel*epsocc) EXIT
     547              : 
     548        63764 :          IF (N_now <= nel) THEN
     549        39714 :             mu_min = mu
     550              :          ELSE
     551        24050 :             mu_max = mu
     552              :          END IF
     553              : 
     554        94134 :          IF (iter > BISECT_MAX_ITER) THEN
     555            0 :             CPWARN("Smearkp: maximum bisection iterations reached")
     556            0 :             EXIT
     557              :          END IF
     558              :       END DO
     559        30370 :       mu = (mu_max + mu_min)/2.0_dp
     560              : 
     561              :       ! Damped Newton refinement for non-monotonic methods.  Accept only
     562              :       ! residual-reducing steps and limit the displacement from the local
     563              :       ! Gaussian solution to avoid jumping to a remote MP/MV root.
     564           56 :       SELECT CASE (method)
     565              :       CASE (smear_mp, smear_mv)
     566           56 :          CALL Smear2(f, N_now, kTS, e, mu, wk, sigma, maxocc, method)
     567           56 :          res_best = ABS(N_now - nel)
     568           56 :          mu_best = mu
     569          252 :          DO iter = 1, NEWTON_MAX_ITER
     570          252 :             res_now = ABS(N_now - nel)
     571          252 :             IF (res_now < nel*epsocc) EXIT
     572              : 
     573              :             ! Compute dN/dmu = sum_{ik} wk * g_i(k)  inline
     574              :             dNdmu = 0.0_dp
     575         6468 :             DO ik = 1, nkp
     576        69188 :                DO is = 1, nmo
     577        62720 :                   x = (e(is, ik) - mu)/sigma
     578         6272 :                   SELECT CASE (method)
     579              :                   CASE (smear_mp)
     580        26880 :                      expx2 = EXP(-x*x)
     581        26880 :                      dNdmu = dNdmu + maxocc*(3.0_dp - 2.0_dp*x*x)/(2.0_dp*sigma*rootpi)*expx2*wk(ik)
     582              :                   CASE (smear_mv)
     583        35840 :                      u = x + sqrthalf
     584        35840 :                      expu2 = EXP(-u*u)
     585        62720 :                      dNdmu = dNdmu + maxocc*(2.0_dp + sqrt2*x)/(sigma*rootpi)*expu2*wk(ik)
     586              :                   END SELECT
     587              :                END DO
     588              :             END DO
     589              : 
     590          196 :             IF (ABS(dNdmu) < EPSILON(dNdmu)) EXIT
     591          196 :             step = (nel - N_now)/dNdmu
     592          196 :             step = SIGN(MIN(ABS(step), MPMV_MAX_NEWTON_STEP*sigma), step)
     593          196 :             step_try = step
     594          196 :             DO iback = 1, NEWTON_MAX_BACKTRACK
     595          196 :                CALL Smear2(f, N_trial, kTS, e, mu + step_try, wk, sigma, maxocc, method)
     596          196 :                res_trial = ABS(N_trial - nel)
     597          196 :                IF (res_trial < res_now) THEN
     598          196 :                   mu = mu + step_try
     599          196 :                   N_now = N_trial
     600          196 :                   IF (res_trial < res_best) THEN
     601          196 :                      res_best = res_trial
     602          196 :                      mu_best = mu
     603              :                   END IF
     604              :                   EXIT
     605              :                END IF
     606            0 :                step_try = 0.5_dp*step_try
     607              :             END DO
     608           56 :             IF (iback > NEWTON_MAX_BACKTRACK) EXIT
     609              :          END DO
     610        30426 :          mu = mu_best
     611              :       END SELECT
     612              : 
     613              :       ! Final evaluation with the actual method
     614        30370 :       CALL Smear2(f, N_now, kTS, e, mu, wk, sigma, maxocc, method)
     615           56 :       SELECT CASE (method)
     616              :       CASE (smear_mp, smear_mv)
     617        30370 :          IF (ABS(N_now - nel) >= nel*epsocc) THEN
     618            0 :             CPWARN("Smearkp: MP/MV smearing did not reach the requested electron count")
     619              :          END IF
     620              :       END SELECT
     621              : 
     622        30370 :    END SUBROUTINE Smearkp
     623              : 
     624              : ! **************************************************************************************************
     625              : !> \brief   Bisection search for mu (k-point, spin-polarised with a shared
     626              : !>          chemical potential across both spin channels).
     627              : !>          Asserts that the third dimension of f and e is exactly 2.
     628              : !>
     629              : !>          For MP-1 and MV: Gaussian bisection + Newton refinement.
     630              : !>
     631              : !> \param f       occupations (nmo x nkp x 2, output)
     632              : !> \param mu      chemical potential (output)
     633              : !> \param kTS     smearing correction (output)
     634              : !> \param e       eigenvalues (nmo x nkp x 2, input)
     635              : !> \param nel     target total number of electrons (input)
     636              : !> \param wk      k-point weights (input)
     637              : !> \param sigma   smearing width (input)
     638              : !> \param method  smearing method selector (input)
     639              : ! **************************************************************************************************
     640           32 :    SUBROUTINE Smearkp2(f, mu, kTS, e, nel, wk, sigma, method)
     641              : 
     642              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(OUT)     :: f
     643              :       REAL(KIND=dp), INTENT(OUT)                         :: mu, kTS
     644              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(IN)      :: e
     645              :       REAL(KIND=dp), INTENT(IN)                          :: nel
     646              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: wk
     647              :       REAL(KIND=dp), INTENT(IN)                          :: sigma
     648              :       INTEGER, INTENT(IN)                                :: method
     649              : 
     650              :       REAL(KIND=dp), PARAMETER                           :: epsocc = 1.0e-12_dp
     651              : 
     652              :       INTEGER                                            :: bisect_method, iback, ik, is, ispin, &
     653              :                                                             iter, nkp, nmo
     654              :       REAL(KIND=dp) :: de, dNdmu, expu2, expx2, kTSa, kTSb, mu_best, mu_max, mu_min, N_now, &
     655              :          N_trial, na, nb, res_best, res_now, res_trial, step, step_try, u, x
     656              : 
     657           32 :       CPASSERT(SIZE(f, 3) == 2 .AND. SIZE(e, 3) == 2)
     658              : 
     659           32 :       nmo = SIZE(e, 1)
     660           32 :       nkp = SIZE(e, 2)
     661              : 
     662           32 :       CALL cite_smearing(method)
     663              : 
     664           32 :       SELECT CASE (method)
     665              :       CASE (smear_mp, smear_mv)
     666            0 :          bisect_method = smear_gaussian
     667              :       CASE DEFAULT
     668           32 :          bisect_method = method
     669              :       END SELECT
     670              : 
     671            0 :       SELECT CASE (bisect_method)
     672              :       CASE (smear_fermi_dirac)
     673            0 :          de = sigma*LOG((1.0_dp - epsocc)/epsocc)
     674              :       CASE DEFAULT
     675           32 :          de = 10.0_dp*sigma
     676              :       END SELECT
     677           32 :       de = MAX(de, 0.5_dp)
     678              : 
     679              :       ! Bisection with bisect_method
     680         2472 :       mu_min = MINVAL(e) - de
     681         2472 :       mu_max = MAXVAL(e) + de
     682           32 :       iter = 0
     683         1036 :       DO WHILE (mu_max - mu_min > EPSILON(mu)*MAX(1.0_dp, ABS(mu_max), ABS(mu_min)))
     684         1036 :          iter = iter + 1
     685         1036 :          mu = (mu_max + mu_min)/2.0_dp
     686         1036 :          CALL Smear2(f(:, :, 1), na, kTSa, e(:, :, 1), mu, wk, sigma, 1.0_dp, bisect_method)
     687         1036 :          CALL Smear2(f(:, :, 2), nb, kTSb, e(:, :, 2), mu, wk, sigma, 1.0_dp, bisect_method)
     688         1036 :          N_now = na + nb
     689              : 
     690         1036 :          IF (ABS(N_now - nel) < nel*epsocc) EXIT
     691              : 
     692         1004 :          IF (N_now <= nel) THEN
     693          494 :             mu_min = mu
     694              :          ELSE
     695          510 :             mu_max = mu
     696              :          END IF
     697              : 
     698         1036 :          IF (iter > BISECT_MAX_ITER) THEN
     699            0 :             CPWARN("Smearkp2: maximum bisection iterations reached")
     700            0 :             EXIT
     701              :          END IF
     702              :       END DO
     703           32 :       mu = (mu_max + mu_min)/2.0_dp
     704              : 
     705              :       ! Damped Newton refinement for non-monotonic methods.  Accept only
     706              :       ! residual-reducing steps and limit the displacement from the local
     707              :       ! Gaussian solution to avoid jumping to a remote MP/MV root.
     708            0 :       SELECT CASE (method)
     709              :       CASE (smear_mp, smear_mv)
     710            0 :          CALL Smear2(f(:, :, 1), na, kTSa, e(:, :, 1), mu, wk, sigma, 1.0_dp, method)
     711            0 :          CALL Smear2(f(:, :, 2), nb, kTSb, e(:, :, 2), mu, wk, sigma, 1.0_dp, method)
     712            0 :          N_now = na + nb
     713            0 :          res_best = ABS(N_now - nel)
     714            0 :          mu_best = mu
     715            0 :          DO iter = 1, NEWTON_MAX_ITER
     716            0 :             res_now = ABS(N_now - nel)
     717            0 :             IF (res_now < nel*epsocc) EXIT
     718              : 
     719              :             ! dN/dmu across both spin channels (maxocc=1 per spin)
     720              :             dNdmu = 0.0_dp
     721            0 :             DO ispin = 1, 2
     722            0 :                DO ik = 1, nkp
     723            0 :                   DO is = 1, nmo
     724            0 :                      x = (e(is, ik, ispin) - mu)/sigma
     725            0 :                      SELECT CASE (method)
     726              :                      CASE (smear_mp)
     727            0 :                         expx2 = EXP(-x*x)
     728            0 :                         dNdmu = dNdmu + (3.0_dp - 2.0_dp*x*x)/(2.0_dp*sigma*rootpi)*expx2*wk(ik)
     729              :                      CASE (smear_mv)
     730            0 :                         u = x + sqrthalf
     731            0 :                         expu2 = EXP(-u*u)
     732            0 :                         dNdmu = dNdmu + (2.0_dp + sqrt2*x)/(sigma*rootpi)*expu2*wk(ik)
     733              :                      END SELECT
     734              :                   END DO
     735              :                END DO
     736              :             END DO
     737              : 
     738            0 :             IF (ABS(dNdmu) < EPSILON(dNdmu)) EXIT
     739            0 :             step = (nel - N_now)/dNdmu
     740            0 :             step = SIGN(MIN(ABS(step), MPMV_MAX_NEWTON_STEP*sigma), step)
     741            0 :             step_try = step
     742            0 :             DO iback = 1, NEWTON_MAX_BACKTRACK
     743            0 :                CALL Smear2(f(:, :, 1), na, kTSa, e(:, :, 1), mu + step_try, wk, sigma, 1.0_dp, method)
     744            0 :                CALL Smear2(f(:, :, 2), nb, kTSb, e(:, :, 2), mu + step_try, wk, sigma, 1.0_dp, method)
     745            0 :                N_trial = na + nb
     746            0 :                res_trial = ABS(N_trial - nel)
     747            0 :                IF (res_trial < res_now) THEN
     748            0 :                   mu = mu + step_try
     749            0 :                   N_now = N_trial
     750            0 :                   IF (res_trial < res_best) THEN
     751            0 :                      res_best = res_trial
     752            0 :                      mu_best = mu
     753              :                   END IF
     754              :                   EXIT
     755              :                END IF
     756            0 :                step_try = 0.5_dp*step_try
     757              :             END DO
     758            0 :             IF (iback > NEWTON_MAX_BACKTRACK) EXIT
     759              :          END DO
     760           32 :          mu = mu_best
     761              :       END SELECT
     762              : 
     763              :       ! Final evaluation with the actual method
     764           32 :       CALL Smear2(f(:, :, 1), na, kTSa, e(:, :, 1), mu, wk, sigma, 1.0_dp, method)
     765           32 :       CALL Smear2(f(:, :, 2), nb, kTSb, e(:, :, 2), mu, wk, sigma, 1.0_dp, method)
     766           32 :       N_now = na + nb
     767           32 :       kTS = kTSa + kTSb
     768            0 :       SELECT CASE (method)
     769              :       CASE (smear_mp, smear_mv)
     770           32 :          IF (ABS(N_now - nel) >= nel*epsocc) THEN
     771            0 :             CPWARN("Smearkp2: MP/MV smearing did not reach the requested electron count")
     772              :          END IF
     773              :       END SELECT
     774              : 
     775           32 :    END SUBROUTINE Smearkp2
     776              : 
     777              : ! **************************************************************************************************
     778              : !> \brief   Computes the smearing weight vector g_i = -df_i/de_i with mu held
     779              : !>          fixed (module-private helper for the analytical Jacobian routines).
     780              : !>
     781              : !>          Fermi-Dirac:  g_i = occ * f_norm * (1 - f_norm) / sigma
     782              : !>                        where f_norm = f_i/occ_i  (overflow-safe, uses
     783              : !>                        pre-computed f rather than re-evaluating exp)
     784              : !>          Gaussian:     g_i = occ / (sigma*sqrt(pi)) * exp(-x^2)
     785              : !>          MP-1:         g_i = occ * (3 - 2*x^2) / (2*sigma*sqrt(pi)) * exp(-x^2)
     786              : !>          MV:           g_i = occ * (2 + sqrt(2)*x) / (sigma*sqrt(pi)) * exp(-u^2)
     787              : !>
     788              : !>          Note: g_i can be negative for MP-1 (|x| > sqrt(3/2)) and MV
     789              : !>          (x < -sqrt(2)).  Consequently, N(mu) is not guaranteed to be
     790              : !>          monotone and the Jacobian routines must guard against G = sum(g_i)
     791              : !>          being near zero.
     792              : !>
     793              : !> \param gvec    weight vector (Nstate, output)
     794              : !> \param f       occupations from a prior SmearOcc/SmearFixed call (input)
     795              : !> \param e       eigenvalues (input)
     796              : !> \param mu      chemical potential (input)
     797              : !> \param sigma   smearing width (input)
     798              : !> \param maxocc  maximum occupation (input)
     799              : !> \param Nstate  number of states (input)
     800              : !> \param method  smearing method selector (input)
     801              : !> \param estate  excited state index (optional)
     802              : !> \param festate occupation of the excited state (optional)
     803              : ! **************************************************************************************************
     804            0 :    SUBROUTINE compute_gvec(gvec, f, e, mu, sigma, maxocc, Nstate, method, estate, festate)
     805              : 
     806              :       REAL(KIND=dp), INTENT(OUT)                         :: gvec(:)
     807              :       REAL(KIND=dp), INTENT(IN)                          :: f(:), e(:), mu, sigma, maxocc
     808              :       INTEGER, INTENT(IN)                                :: Nstate, method
     809              :       INTEGER, INTENT(IN), OPTIONAL                      :: estate
     810              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: festate
     811              : 
     812              :       INTEGER                                            :: i
     813              :       REAL(KIND=dp)                                      :: expu2, expx2, fi_norm, occ_i, u, x
     814              : 
     815            0 :       DO i = 1, Nstate
     816            0 :          IF (PRESENT(estate) .AND. PRESENT(festate)) THEN
     817            0 :             IF (i == estate) THEN
     818            0 :                occ_i = festate
     819              :             ELSE
     820            0 :                occ_i = maxocc
     821              :             END IF
     822              :          ELSE
     823            0 :             occ_i = maxocc
     824              :          END IF
     825              : 
     826            0 :          IF (occ_i < EPSILON(occ_i)) THEN
     827            0 :             gvec(i) = 0.0_dp
     828            0 :             CYCLE
     829              :          END IF
     830              : 
     831            0 :          x = (e(i) - mu)/sigma
     832              : 
     833            0 :          SELECT CASE (method)
     834              :          CASE (smear_fermi_dirac)
     835            0 :             fi_norm = f(i)/occ_i
     836            0 :             gvec(i) = occ_i*fi_norm*(1.0_dp - fi_norm)/sigma
     837              : 
     838              :          CASE (smear_gaussian)
     839            0 :             expx2 = EXP(-x*x)
     840            0 :             gvec(i) = occ_i/(sigma*rootpi)*expx2
     841              : 
     842              :          CASE (smear_mp)
     843            0 :             expx2 = EXP(-x*x)
     844            0 :             gvec(i) = occ_i*(3.0_dp - 2.0_dp*x*x)/(2.0_dp*sigma*rootpi)*expx2
     845              : 
     846              :          CASE (smear_mv)
     847            0 :             u = x + sqrthalf
     848            0 :             expu2 = EXP(-u*u)
     849            0 :             gvec(i) = occ_i*(2.0_dp + sqrt2*x)/(sigma*rootpi)*expu2
     850              : 
     851              :          END SELECT
     852              :       END DO
     853              : 
     854            0 :    END SUBROUTINE compute_gvec
     855              : 
     856              : ! **************************************************************************************************
     857              : !> \brief   Analytical Jacobian df_i/de_j for any smearing method under the
     858              : !>          electron-number constraint sum(f) = N.
     859              : !>
     860              : !>          Differentiating f_i(e, mu(e)) where mu is implicitly defined by
     861              : !>          the constraint yields:
     862              : !>
     863              : !>            df_i/de_j  =  -delta_{ij} * g_i  +  g_i * g_j / G
     864              : !>
     865              : !>          where g_i = -df_i/de_i (mu fixed) and G = sum(g_i).
     866              : !>          This is a diagonal matrix plus a symmetric rank-1 update.
     867              : !>          Building it costs O(N) for g, plus O(N^2) for the outer product.
     868              : !>
     869              : !>          Replaces the original numerical finite-difference FermiFixedDeriv
     870              : !>          which required 2N bisection solves.  Exact to machine precision
     871              : !>          for all four methods.
     872              : !>
     873              : !> \param dfde    Jacobian matrix dfde(i,j) = df_i/de_j (Nstate x Nstate, output)
     874              : !> \param f       occupations (output)
     875              : !> \param mu      chemical potential (output)
     876              : !> \param kTS     smearing correction (output)
     877              : !> \param e       eigenvalues (input)
     878              : !> \param N       target number of electrons (input)
     879              : !> \param sigma   smearing width (input)
     880              : !> \param maxocc  maximum occupation (input)
     881              : !> \param method  smearing method selector (input)
     882              : !> \param estate  excited state index (optional)
     883              : !> \param festate occupation of the excited state (optional)
     884              : ! **************************************************************************************************
     885            0 :    SUBROUTINE SmearFixedDeriv(dfde, f, mu, kTS, e, N, sigma, maxocc, method, estate, festate)
     886              : 
     887              :       REAL(KIND=dp), INTENT(OUT)                         :: dfde(:, :), f(:), mu, kTS
     888              :       REAL(KIND=dp), INTENT(IN)                          :: e(:), N, sigma, maxocc
     889              :       INTEGER, INTENT(IN)                                :: method
     890              :       INTEGER, INTENT(IN), OPTIONAL                      :: estate
     891              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: festate
     892              : 
     893              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'SmearFixedDeriv'
     894              : 
     895              :       INTEGER                                            :: handle, i, j, Nstate
     896              :       REAL(KIND=dp)                                      :: Gsum
     897            0 :       REAL(KIND=dp), ALLOCATABLE                         :: gvec(:)
     898              : 
     899            0 :       CALL timeset(routineN, handle)
     900              : 
     901              :       ! Step 1: find mu and f
     902            0 :       CALL SmearFixed(f, mu, kTS, e, N, sigma, maxocc, method, estate, festate)
     903              : 
     904              :       ! Step 2: build g vector
     905            0 :       Nstate = SIZE(e)
     906            0 :       ALLOCATE (gvec(Nstate))
     907            0 :       CALL compute_gvec(gvec, f, e, mu, sigma, maxocc, Nstate, method, estate, festate)
     908            0 :       Gsum = accurate_sum(gvec)
     909              : 
     910              :       ! Step 3: assemble dfde(i,j) = -delta_{ij}*g_i + g_i*g_j/G
     911            0 :       IF (ABS(Gsum) > EPSILON(Gsum)) THEN
     912            0 :          DO j = 1, Nstate
     913            0 :             DO i = 1, Nstate
     914            0 :                dfde(i, j) = gvec(i)*gvec(j)/Gsum
     915              :             END DO
     916            0 :             dfde(j, j) = dfde(j, j) - gvec(j)
     917              :          END DO
     918              :       ELSE
     919            0 :          dfde(:, :) = 0.0_dp
     920              :       END IF
     921              : 
     922            0 :       DEALLOCATE (gvec)
     923            0 :       CALL timestop(handle)
     924              : 
     925            0 :    END SUBROUTINE SmearFixedDeriv
     926              : 
     927              : ! **************************************************************************************************
     928              : !> \brief   Apply TRANSPOSE(df/de) to a vector WITHOUT forming the full N x N
     929              : !>          Jacobian.  O(N) time and O(N) memory for all four methods.
     930              : !>
     931              : !>          Exploiting the rank-1 structure of the constrained Jacobian:
     932              : !>
     933              : !>            [J^T v]_j  =  g_j * (g . v / G  -  v_j)
     934              : !>
     935              : !>          This replaces the pattern used in qs_mo_occupation:
     936              : !>            ALLOCATE(dfde(nmo,nmo))
     937              : !>            CALL SmearFixedDeriv(dfde, ...)
     938              : !>            RESULT = MATMUL(TRANSPOSE(dfde), v)
     939              : !>            DEALLOCATE(dfde)
     940              : !>          turning O(N^2) storage + O(N^2) MATMUL into O(N) throughout.
     941              : !>
     942              : !>          Currently the sole caller (qs_ot_scf do_ener) is dead code, but
     943              : !>          this routine is ready for when it is enabled.
     944              : !>
     945              : !> \param RESULT  output vector = TRANSPOSE(df/de) * v (Nstate, output)
     946              : !> \param f       occupations (output)
     947              : !> \param mu      chemical potential (output)
     948              : !> \param kTS     smearing correction (output)
     949              : !> \param e       eigenvalues (input)
     950              : !> \param N_el    target number of electrons (input)
     951              : !> \param sigma   smearing width (input)
     952              : !> \param maxocc  maximum occupation (input)
     953              : !> \param method  smearing method selector (input)
     954              : !> \param v       input vector to multiply (Nstate, input)
     955              : !> \param estate  excited state index (optional)
     956              : !> \param festate occupation of the excited state (optional)
     957              : ! **************************************************************************************************
     958            0 :    SUBROUTINE SmearFixedDerivMV(RESULT, f, mu, kTS, e, N_el, sigma, maxocc, method, v, estate, festate)
     959              : 
     960              :       REAL(KIND=dp), INTENT(OUT)                         :: RESULT(:), f(:), mu, kTS
     961              :       REAL(KIND=dp), INTENT(IN)                          :: e(:), N_el, sigma, maxocc
     962              :       INTEGER, INTENT(IN)                                :: method
     963              :       REAL(KIND=dp), INTENT(IN)                          :: v(:)
     964              :       INTEGER, INTENT(IN), OPTIONAL                      :: estate
     965              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: festate
     966              : 
     967              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'SmearFixedDerivMV'
     968              : 
     969              :       INTEGER                                            :: handle, i, Nstate
     970              :       REAL(KIND=dp)                                      :: gdotv, Gsum
     971            0 :       REAL(KIND=dp), ALLOCATABLE                         :: gvec(:)
     972              : 
     973            0 :       CALL timeset(routineN, handle)
     974              : 
     975              :       ! Step 1: find mu and f
     976            0 :       CALL SmearFixed(f, mu, kTS, e, N_el, sigma, maxocc, method, estate, festate)
     977              : 
     978              :       ! Step 2: build g vector
     979            0 :       Nstate = SIZE(e)
     980            0 :       ALLOCATE (gvec(Nstate))
     981            0 :       CALL compute_gvec(gvec, f, e, mu, sigma, maxocc, Nstate, method, estate, festate)
     982            0 :       Gsum = accurate_sum(gvec)
     983              : 
     984              :       ! Step 3: RESULT_j = g_j * (g.v / G - v_j)
     985            0 :       IF (ABS(Gsum) > EPSILON(Gsum)) THEN
     986              :          gdotv = 0.0_dp
     987            0 :          DO i = 1, Nstate
     988            0 :             gdotv = gdotv + gvec(i)*v(i)
     989              :          END DO
     990            0 :          DO i = 1, Nstate
     991            0 :             RESULT(i) = gvec(i)*(gdotv/Gsum - v(i))
     992              :          END DO
     993              :       ELSE
     994            0 :          RESULT(:) = 0.0_dp
     995              :       END IF
     996              : 
     997            0 :       DEALLOCATE (gvec)
     998            0 :       CALL timestop(handle)
     999              : 
    1000            0 :    END SUBROUTINE SmearFixedDerivMV
    1001              : 
    1002              : END MODULE smearing_utils
        

Generated by: LCOV version 2.0-1