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

Generated by: LCOV version 2.0-1