LCOV - code coverage report
Current view: top level - src - qs_mo_occupation.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:1155b05) Lines: 81.1 % 354 287
Test Date: 2026-03-21 06:31:29 Functions: 100.0 % 3 3

            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 Set occupation of molecular orbitals
      10              : !> \par History
      11              : !>      - set_mo_occupation subroutines moved from qs_mo_types (11.12.2014 MI)
      12              : !> \author  MI
      13              : ! **************************************************************************************************
      14              : 
      15              : MODULE qs_mo_occupation
      16              : 
      17              :    USE cp_control_types,                ONLY: hairy_probes_type
      18              :    USE cp_log_handling,                 ONLY: cp_to_string
      19              :    USE hairy_probes,                    ONLY: probe_occupancy
      20              :    USE input_constants,                 ONLY: smear_energy_window,&
      21              :                                               smear_fermi_dirac,&
      22              :                                               smear_gaussian,&
      23              :                                               smear_list,&
      24              :                                               smear_mp,&
      25              :                                               smear_mv
      26              :    USE kahan_sum,                       ONLY: accurate_sum
      27              :    USE kinds,                           ONLY: dp
      28              :    USE qs_mo_types,                     ONLY: get_mo_set,&
      29              :                                               has_uniform_occupation,&
      30              :                                               mo_set_type,&
      31              :                                               set_mo_set
      32              :    USE scf_control_types,               ONLY: smear_type
      33              :    USE smearing_utils,                  ONLY: SmearFixed,&
      34              :                                               SmearFixedDerivMV
      35              :    USE util,                            ONLY: sort
      36              :    USE xas_env_types,                   ONLY: get_xas_env,&
      37              :                                               xas_environment_type
      38              : #include "./base/base_uses.f90"
      39              : 
      40              :    IMPLICIT NONE
      41              : 
      42              :    PRIVATE
      43              : 
      44              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_mo_occupation'
      45              : 
      46              :    PUBLIC :: set_mo_occupation
      47              : 
      48              :    INTERFACE set_mo_occupation
      49              :       MODULE PROCEDURE set_mo_occupation_1, set_mo_occupation_2
      50              :    END INTERFACE
      51              : 
      52              : CONTAINS
      53              : 
      54              : ! **************************************************************************************************
      55              : !> \brief  Occupation for smeared spin polarized electronic structures
      56              : !>         with relaxed multiplicity
      57              : !>
      58              : !> \param mo_array ...
      59              : !> \param smear ...
      60              : !> \date    10.03.2011 (MI)
      61              : !> \author  MI
      62              : !> \version 1.0
      63              : ! **************************************************************************************************
      64          180 :    SUBROUTINE set_mo_occupation_3(mo_array, smear)
      65              : 
      66              :       TYPE(mo_set_type), DIMENSION(2), INTENT(INOUT)     :: mo_array
      67              :       TYPE(smear_type)                                   :: smear
      68              : 
      69              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_3'
      70              : 
      71              :       INTEGER                                            :: all_nmo, handle, homo_a, homo_b, i, &
      72              :                                                             lfomo_a, lfomo_b, nmo_a, nmo_b, &
      73              :                                                             xas_estate
      74          180 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: all_index
      75              :       LOGICAL                                            :: is_large
      76              :       REAL(KIND=dp)                                      :: all_nelec, kTS, mu, nelec_a, nelec_b, &
      77              :                                                             occ_estate
      78              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: all_eigval, all_occ
      79          180 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: eigval_a, eigval_b, occ_a, occ_b
      80              : 
      81          180 :       CALL timeset(routineN, handle)
      82              : 
      83          180 :       NULLIFY (eigval_a, eigval_b, occ_a, occ_b)
      84              :       CALL get_mo_set(mo_set=mo_array(1), nmo=nmo_a, eigenvalues=eigval_a, &
      85          180 :                       occupation_numbers=occ_a)
      86              :       CALL get_mo_set(mo_set=mo_array(2), nmo=nmo_b, eigenvalues=eigval_b, &
      87          180 :                       occupation_numbers=occ_b)
      88          180 :       all_nmo = nmo_a + nmo_b
      89          540 :       ALLOCATE (all_eigval(all_nmo))
      90          360 :       ALLOCATE (all_occ(all_nmo))
      91          540 :       ALLOCATE (all_index(all_nmo))
      92              : 
      93         4628 :       all_eigval(1:nmo_a) = eigval_a(1:nmo_a)
      94         4628 :       all_eigval(nmo_a + 1:all_nmo) = eigval_b(1:nmo_b)
      95              : 
      96          180 :       CALL sort(all_eigval, all_nmo, all_index)
      97              : 
      98          180 :       xas_estate = -1
      99          180 :       occ_estate = 0.0_dp
     100              : 
     101              :       nelec_a = 0.0_dp
     102              :       nelec_b = 0.0_dp
     103              :       all_nelec = 0.0_dp
     104          180 :       nelec_a = accurate_sum(occ_a(:))
     105          180 :       nelec_b = accurate_sum(occ_b(:))
     106          180 :       all_nelec = nelec_a + nelec_b
     107              : 
     108         9076 :       DO i = 1, all_nmo
     109         9076 :          IF (all_index(i) <= nmo_a) THEN
     110         4448 :             all_occ(i) = occ_a(all_index(i))
     111              :          ELSE
     112         4448 :             all_occ(i) = occ_b(all_index(i) - nmo_a)
     113              :          END IF
     114              :       END DO
     115              : 
     116              :       CALL SmearFixed(all_occ, mu, kTS, all_eigval, all_nelec, &
     117          180 :                       smear%electronic_temperature, 1._dp, smear%method, xas_estate, occ_estate)
     118              : 
     119         9256 :       is_large = ABS(MAXVAL(all_occ) - 1.0_dp) > smear%eps_fermi_dirac
     120              :       ! this is not a real problem, but the temperature might be a bit large
     121          180 :       CPWARN_IF(is_large, "Fermi-Dirac smearing includes the first MO")
     122              : 
     123         9256 :       is_large = ABS(MINVAL(all_occ)) > smear%eps_fermi_dirac
     124          180 :       IF (is_large) &
     125              :          CALL cp_warn(__LOCATION__, &
     126              :                       "Fermi-Dirac smearing includes the last MO => "// &
     127           20 :                       "Add more MOs for proper smearing.")
     128              : 
     129              :       ! check that the total electron count is accurate
     130          180 :       is_large = (ABS(all_nelec - accurate_sum(all_occ(:))) > smear%eps_fermi_dirac*all_nelec)
     131          180 :       CPWARN_IF(is_large, "Total number of electrons is not accurate")
     132              : 
     133         9076 :       DO i = 1, all_nmo
     134         9076 :          IF (all_index(i) <= nmo_a) THEN
     135         4448 :             occ_a(all_index(i)) = all_occ(i)
     136         4448 :             eigval_a(all_index(i)) = all_eigval(i)
     137              :          ELSE
     138         4448 :             occ_b(all_index(i) - nmo_a) = all_occ(i)
     139         4448 :             eigval_b(all_index(i) - nmo_a) = all_eigval(i)
     140              :          END IF
     141              :       END DO
     142              : 
     143          180 :       nelec_a = accurate_sum(occ_a(:))
     144          180 :       nelec_b = accurate_sum(occ_b(:))
     145              : 
     146         2058 :       DO i = 1, nmo_a
     147         2058 :          IF (occ_a(i) < 1.0_dp) THEN
     148          180 :             lfomo_a = i
     149          180 :             EXIT
     150              :          END IF
     151              :       END DO
     152         1782 :       DO i = 1, nmo_b
     153         1782 :          IF (occ_b(i) < 1.0_dp) THEN
     154          180 :             lfomo_b = i
     155          180 :             EXIT
     156              :          END IF
     157              :       END DO
     158          180 :       homo_a = lfomo_a - 1
     159         2250 :       DO i = nmo_a, lfomo_a, -1
     160         2250 :          IF (occ_a(i) > smear%eps_fermi_dirac) THEN
     161           88 :             homo_a = i
     162           88 :             EXIT
     163              :          END IF
     164              :       END DO
     165          180 :       homo_b = lfomo_b - 1
     166         2526 :       DO i = nmo_b, lfomo_b, -1
     167         2526 :          IF (occ_b(i) > smear%eps_fermi_dirac) THEN
     168           88 :             homo_b = i
     169           88 :             EXIT
     170              :          END IF
     171              :       END DO
     172              : 
     173              :       CALL set_mo_set(mo_set=mo_array(1), kTS=kTS/2.0_dp, mu=mu, n_el_f=nelec_a, &
     174          180 :                       lfomo=lfomo_a, homo=homo_a, uniform_occupation=.FALSE.)
     175              :       CALL set_mo_set(mo_set=mo_array(2), kTS=kTS/2.0_dp, mu=mu, n_el_f=nelec_b, &
     176          180 :                       lfomo=lfomo_b, homo=homo_b, uniform_occupation=.FALSE.)
     177              : 
     178          180 :       CALL timestop(handle)
     179              : 
     180          360 :    END SUBROUTINE set_mo_occupation_3
     181              : 
     182              : ! **************************************************************************************************
     183              : !> \brief   Prepare an occupation of alpha and beta MOs following an Aufbau
     184              : !>          principle, i.e. allowing a change in multiplicity.
     185              : !> \param mo_array ...
     186              : !> \param smear ...
     187              : !> \param eval_deriv ...
     188              : !> \param tot_zeff_corr ...
     189              : !> \param probe ...
     190              : !> \date    25.01.2010 (MK)
     191              : !> \par   History
     192              : !>        10.2019 Added functionality to adjust mo occupation if the core
     193              : !>                charges are changed via CORE_CORRECTION during surface dipole
     194              : !>                calculation. Total number of electrons matches the total core
     195              : !>                charges if tot_zeff_corr is non-zero. Not yet implemented for
     196              : !>                OT type method. [Soumya Ghosh]
     197              : !> \author  Matthias Krack (MK)
     198              : !> \version 1.0
     199              : ! **************************************************************************************************
     200        95479 :    SUBROUTINE set_mo_occupation_2(mo_array, smear, eval_deriv, tot_zeff_corr, probe)
     201              : 
     202              :       TYPE(mo_set_type), DIMENSION(:), INTENT(INOUT)     :: mo_array
     203              :       TYPE(smear_type)                                   :: smear
     204              :       REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER     :: eval_deriv
     205              :       REAL(KIND=dp), OPTIONAL                            :: tot_zeff_corr
     206              :       TYPE(hairy_probes_type), DIMENSION(:), OPTIONAL, &
     207              :          POINTER                                         :: probe
     208              : 
     209              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_2'
     210              : 
     211              :       INTEGER                                            :: handle, i, lumo_a, lumo_b, &
     212              :                                                             multiplicity_new, multiplicity_old, &
     213              :                                                             nelec
     214              :       REAL(KIND=dp)                                      :: nelec_f, threshold
     215        95479 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: eigval_a, eigval_b
     216              : 
     217        95479 :       CALL timeset(routineN, handle)
     218              : 
     219              :       ! Fall back for the case that we have only one MO set
     220        95479 :       IF (SIZE(mo_array) == 1) THEN
     221        85303 :          IF (PRESENT(probe)) THEN
     222           14 :             CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
     223        85289 :          ELSE IF (PRESENT(eval_deriv)) THEN
     224              : ! Change of MO occupancy to account for CORE_CORRECTION is not yet implemented
     225            0 :             CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
     226              :          ELSE
     227        85289 :             IF (PRESENT(tot_zeff_corr)) THEN
     228           20 :                CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
     229              :             ELSE
     230        85269 :                CALL set_mo_occupation_1(mo_array(1), smear=smear)
     231              :             END IF
     232              :          END IF
     233        85303 :          CALL timestop(handle)
     234              :          RETURN
     235              :       END IF
     236              : 
     237        10176 :       IF (PRESENT(probe)) THEN
     238            0 :          CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
     239            0 :          CALL set_mo_occupation_1(mo_array(2), smear=smear, probe=probe)
     240              :       END IF
     241              : 
     242        10176 :       IF (smear%do_smear) THEN
     243         1536 :          IF (smear%fixed_mag_mom < 0.0_dp) THEN
     244          180 :             IF (PRESENT(tot_zeff_corr)) THEN
     245              :                CALL cp_warn(__LOCATION__, &
     246              :                             "CORE_CORRECTION /= 0.0 might cause the cell to charge up "// &
     247              :                             "that will lead to application of different background "// &
     248              :                             "correction compared to the reference system. "// &
     249              :                             "Use FIXED_MAGNETIC_MOMENT >= 0.0 if using SMEAR keyword "// &
     250            0 :                             "to correct the electron density")
     251              :             END IF
     252          180 :             IF (smear%fixed_mag_mom /= -1.0_dp) THEN
     253          180 :                CPASSERT(.NOT. (PRESENT(eval_deriv)))
     254          180 :                CALL set_mo_occupation_3(mo_array, smear=smear)
     255          180 :                CALL timestop(handle)
     256          180 :                RETURN
     257              :             END IF
     258              :          ELSE
     259         1356 :             nelec_f = mo_array(1)%n_el_f + mo_array(2)%n_el_f
     260         1356 :             IF (ABS((mo_array(1)%n_el_f - mo_array(2)%n_el_f) - smear%fixed_mag_mom) > smear%eps_fermi_dirac*nelec_f) THEN
     261            2 :                mo_array(1)%n_el_f = nelec_f/2.0_dp + smear%fixed_mag_mom/2.0_dp
     262            2 :                mo_array(2)%n_el_f = nelec_f/2.0_dp - smear%fixed_mag_mom/2.0_dp
     263              :             END IF
     264         1356 :             CPASSERT(.NOT. (PRESENT(eval_deriv)))
     265         1356 :             IF (PRESENT(tot_zeff_corr)) THEN
     266           20 :                CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
     267           20 :                CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
     268              :             ELSE
     269         1336 :                CALL set_mo_occupation_1(mo_array(1), smear=smear)
     270         1336 :                CALL set_mo_occupation_1(mo_array(2), smear=smear)
     271              :             END IF
     272              :          END IF
     273              :       END IF
     274              : 
     275         9996 :       IF (.NOT. ((mo_array(1)%flexible_electron_count > 0.0_dp) .AND. &
     276              :                  (mo_array(2)%flexible_electron_count > 0.0_dp))) THEN
     277         9830 :          IF (PRESENT(probe)) THEN
     278            0 :             CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
     279            0 :             CALL set_mo_occupation_1(mo_array(2), smear=smear, probe=probe)
     280         9830 :          ELSE IF (PRESENT(eval_deriv)) THEN
     281            0 :             CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
     282            0 :             CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
     283              :          ELSE
     284         9830 :             IF (PRESENT(tot_zeff_corr)) THEN
     285           20 :                CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
     286           20 :                CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
     287              :             ELSE
     288         9810 :                CALL set_mo_occupation_1(mo_array(1), smear=smear)
     289         9810 :                CALL set_mo_occupation_1(mo_array(2), smear=smear)
     290              :             END IF
     291              :          END IF
     292         9830 :          CALL timestop(handle)
     293         9830 :          RETURN
     294              :       END IF
     295              : 
     296          166 :       nelec = mo_array(1)%nelectron + mo_array(2)%nelectron
     297              : 
     298          166 :       multiplicity_old = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
     299              : 
     300          166 :       IF (mo_array(1)%nelectron >= mo_array(1)%nmo) &
     301              :          CALL cp_warn(__LOCATION__, &
     302              :                       "All alpha MOs are occupied. Add more alpha MOs to "// &
     303            0 :                       "allow for a higher multiplicity")
     304          166 :       IF ((mo_array(2)%nelectron >= mo_array(2)%nmo) .AND. (mo_array(2)%nelectron /= mo_array(1)%nelectron)) &
     305              :          CALL cp_warn(__LOCATION__, "All beta MOs are occupied. Add more beta MOs to "// &
     306            0 :                       "allow for a lower multiplicity")
     307              : 
     308          166 :       eigval_a => mo_array(1)%eigenvalues
     309          166 :       eigval_b => mo_array(2)%eigenvalues
     310              : 
     311          166 :       lumo_a = 1
     312          166 :       lumo_b = 1
     313              : 
     314              :       ! Apply Aufbau principle
     315         2046 :       DO i = 1, nelec
     316              :          ! Threshold is needed to ensure a preference for alpha occupation in the case
     317              :          ! of degeneracy
     318         1880 :          threshold = MAX(mo_array(1)%flexible_electron_count, mo_array(2)%flexible_electron_count)
     319         1880 :          IF ((eigval_a(lumo_a) - threshold) < eigval_b(lumo_b)) THEN
     320         1072 :             lumo_a = lumo_a + 1
     321              :          ELSE
     322          808 :             lumo_b = lumo_b + 1
     323              :          END IF
     324         1880 :          IF (lumo_a > mo_array(1)%nmo) THEN
     325            0 :             IF (i /= nelec) &
     326              :                CALL cp_warn(__LOCATION__, &
     327              :                             "All alpha MOs are occupied. Add more alpha MOs to "// &
     328            0 :                             "allow for a higher multiplicity")
     329            0 :             IF (i < nelec) THEN
     330            0 :                lumo_a = lumo_a - 1
     331            0 :                lumo_b = lumo_b + 1
     332              :             END IF
     333              :          END IF
     334         2046 :          IF (lumo_b > mo_array(2)%nmo) THEN
     335           34 :             IF (lumo_b < lumo_a) &
     336              :                CALL cp_warn(__LOCATION__, &
     337              :                             "All beta MOs are occupied. Add more beta MOs to "// &
     338            0 :                             "allow for a lower multiplicity")
     339           34 :             IF (i < nelec) THEN
     340            6 :                lumo_a = lumo_a + 1
     341            6 :                lumo_b = lumo_b - 1
     342              :             END IF
     343              :          END IF
     344              :       END DO
     345              : 
     346          166 :       mo_array(1)%homo = lumo_a - 1
     347          166 :       mo_array(2)%homo = lumo_b - 1
     348              : 
     349          166 :       IF (mo_array(2)%homo > mo_array(1)%homo) THEN
     350              :          CALL cp_warn(__LOCATION__, &
     351              :                       "More beta ("// &
     352              :                       TRIM(ADJUSTL(cp_to_string(mo_array(2)%homo)))// &
     353              :                       ") than alpha ("// &
     354              :                       TRIM(ADJUSTL(cp_to_string(mo_array(1)%homo)))// &
     355            0 :                       ") MOs are occupied. Resorting to low spin state")
     356            0 :          mo_array(1)%homo = nelec/2 + MODULO(nelec, 2)
     357            0 :          mo_array(2)%homo = nelec/2
     358              :       END IF
     359              : 
     360          166 :       mo_array(1)%nelectron = mo_array(1)%homo
     361          166 :       mo_array(2)%nelectron = mo_array(2)%homo
     362          166 :       multiplicity_new = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
     363              : 
     364          166 :       IF (multiplicity_new /= multiplicity_old) &
     365              :          CALL cp_warn(__LOCATION__, &
     366              :                       "Multiplicity changed from "// &
     367              :                       TRIM(ADJUSTL(cp_to_string(multiplicity_old)))//" to "// &
     368            8 :                       TRIM(ADJUSTL(cp_to_string(multiplicity_new))))
     369              : 
     370          166 :       IF (PRESENT(probe)) THEN
     371            0 :          CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
     372            0 :          CALL set_mo_occupation_1(mo_array(2), smear=smear, probe=probe)
     373          166 :       ELSE IF (PRESENT(eval_deriv)) THEN
     374            0 :          CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
     375            0 :          CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
     376              :       ELSE
     377          166 :          IF (PRESENT(tot_zeff_corr)) THEN
     378            0 :             CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
     379            0 :             CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
     380              :          ELSE
     381          166 :             CALL set_mo_occupation_1(mo_array(1), smear=smear)
     382          166 :             CALL set_mo_occupation_1(mo_array(2), smear=smear)
     383              :          END IF
     384              :       END IF
     385              : 
     386          166 :       CALL timestop(handle)
     387              : 
     388        95479 :    END SUBROUTINE set_mo_occupation_2
     389              : 
     390              : ! **************************************************************************************************
     391              : !> \brief   Smearing of the MO occupation with all kind of occupation numbers
     392              : !> \param   mo_set MO dataset structure
     393              : !> \param   smear optional smearing information
     394              : !> \param   eval_deriv on entry the derivative of the KS energy wrt to the occupation number
     395              : !>                     on exit  the derivative of the full free energy (i.e. KS and entropy) wrt to the eigenvalue
     396              : !> \param xas_env ...
     397              : !> \param tot_zeff_corr ...
     398              : !> \param probe ...
     399              : !> \date    17.04.2002 (v1.0), 26.08.2008 (v1.1)
     400              : !> \par   History
     401              : !>        10.2019 Added functionality to adjust mo occupation if the core
     402              : !>                charges are changed via CORE_CORRECTION during surface dipole
     403              : !>                calculation. Total number of electrons matches the total core
     404              : !>                charges if tot_zeff_corr is non-zero. Not yet implemented for
     405              : !>                OT type method. [Soumya Ghosh]
     406              : !> \author  Matthias Krack
     407              : !> \version 1.1
     408              : ! **************************************************************************************************
     409       226072 :    SUBROUTINE set_mo_occupation_1(mo_set, smear, eval_deriv, xas_env, tot_zeff_corr, probe)
     410              : 
     411              :       TYPE(mo_set_type), INTENT(INOUT)                   :: mo_set
     412              :       TYPE(smear_type), OPTIONAL                         :: smear
     413              :       REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER     :: eval_deriv
     414              :       TYPE(xas_environment_type), OPTIONAL, POINTER      :: xas_env
     415              :       REAL(KIND=dp), OPTIONAL                            :: tot_zeff_corr
     416              :       TYPE(hairy_probes_type), DIMENSION(:), OPTIONAL, &
     417              :          POINTER                                         :: probe
     418              : 
     419              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_1'
     420              : 
     421              :       INTEGER                                            :: handle, i_first, imo, ir, irmo, nmo, &
     422              :                                                             nomo, xas_estate
     423              :       LOGICAL                                            :: equal_size, is_large
     424              :       REAL(KIND=dp)                                      :: delectron, e1, e2, edelta, edist, &
     425              :                                                             el_count, nelec, occ_estate, &
     426              :                                                             total_zeff_corr, xas_nelectron
     427       226072 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: tmp_v
     428              : 
     429       226072 :       CALL timeset(routineN, handle)
     430              : 
     431       226072 :       CPASSERT(ASSOCIATED(mo_set%eigenvalues))
     432       226072 :       CPASSERT(ASSOCIATED(mo_set%occupation_numbers))
     433      2454207 :       mo_set%occupation_numbers(:) = 0.0_dp
     434              : 
     435              :       ! Quick return, if no electrons are available
     436       226072 :       IF (mo_set%nelectron == 0) THEN
     437         1652 :          CALL timestop(handle)
     438         1652 :          RETURN
     439              :       END IF
     440              : 
     441       224420 :       xas_estate = -1
     442       224420 :       occ_estate = 0.0_dp
     443       224420 :       IF (PRESENT(xas_env)) THEN
     444          798 :          CALL get_xas_env(xas_env=xas_env, xas_nelectron=xas_nelectron, occ_estate=occ_estate, xas_estate=xas_estate)
     445          798 :          nomo = CEILING(xas_nelectron + 1.0 - occ_estate - EPSILON(0.0_dp))
     446              : 
     447         8102 :          mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
     448          798 :          IF (xas_estate > 0) mo_set%occupation_numbers(xas_estate) = occ_estate
     449         8102 :          el_count = SUM(mo_set%occupation_numbers(1:nomo))
     450          798 :          IF (el_count > xas_nelectron) &
     451           98 :             mo_set%occupation_numbers(nomo) = mo_set%occupation_numbers(nomo) - (el_count - xas_nelectron)
     452         8102 :          el_count = SUM(mo_set%occupation_numbers(1:nomo))
     453          798 :          is_large = ABS(el_count - xas_nelectron) > xas_nelectron*EPSILON(el_count)
     454          798 :          CPASSERT(.NOT. is_large)
     455              :       ELSE
     456       223622 :          IF (MODULO(mo_set%nelectron, INT(mo_set%maxocc)) == 0) THEN
     457       222580 :             nomo = NINT(mo_set%nelectron/mo_set%maxocc)
     458              :             ! Initialize MO occupations
     459      2131275 :             mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
     460              :          ELSE
     461         1042 :             nomo = INT(mo_set%nelectron/mo_set%maxocc) + 1
     462              :             ! Initialize MO occupations
     463         6648 :             mo_set%occupation_numbers(1:nomo - 1) = mo_set%maxocc
     464         1042 :             mo_set%occupation_numbers(nomo) = mo_set%nelectron - (nomo - 1)*mo_set%maxocc
     465              :          END IF
     466              : ! introduce applied potential correction here
     467              : ! electron density is adjusted according to applied core correction
     468              : ! ref: SS, MT, MWF, JN PRL, 2018, 120, 246801
     469              : ! see whether both surface dipole correction and core correction is present in
     470              : ! the inputfile
     471       223622 :          IF (PRESENT(tot_zeff_corr)) THEN
     472              : ! find the additional core charges
     473          106 :             total_zeff_corr = tot_zeff_corr
     474          106 :             IF (INT(mo_set%maxocc) == 1) total_zeff_corr = total_zeff_corr/2.0_dp
     475          106 :             delectron = 0.0_dp
     476          106 :             IF (total_zeff_corr < 0.0_dp) THEN
     477              : ! remove electron density from the mos
     478          106 :                delectron = ABS(total_zeff_corr) - REAL(mo_set%maxocc, KIND=dp)
     479          106 :                IF (delectron > 0.0_dp) THEN
     480            0 :                   mo_set%occupation_numbers(nomo) = 0.0_dp
     481            0 :                   irmo = CEILING(delectron/REAL(mo_set%maxocc, KIND=dp))
     482            0 :                   DO ir = 1, irmo
     483            0 :                      delectron = delectron - REAL(mo_set%maxocc, KIND=dp)
     484            0 :                      IF (delectron < 0.0_dp) THEN
     485            0 :                         mo_set%occupation_numbers(nomo - ir) = -delectron
     486              :                      ELSE
     487            0 :                         mo_set%occupation_numbers(nomo - ir) = 0.0_dp
     488              :                      END IF
     489              :                   END DO
     490            0 :                   nomo = nomo - irmo
     491            0 :                   IF (mo_set%occupation_numbers(nomo) == 0.0_dp) nomo = nomo - 1
     492          106 :                ELSEIF (delectron < 0.0_dp) THEN
     493          106 :                   mo_set%occupation_numbers(nomo) = -delectron
     494              :                ELSE
     495            0 :                   mo_set%occupation_numbers(nomo) = 0.0_dp
     496            0 :                   nomo = nomo - 1
     497              :                END IF
     498            0 :             ELSEIF (total_zeff_corr > 0.0_dp) THEN
     499              : ! add electron density to the mos
     500            0 :                delectron = total_zeff_corr - REAL(mo_set%maxocc, KIND=dp)
     501            0 :                IF (delectron > 0.0_dp) THEN
     502            0 :                   mo_set%occupation_numbers(nomo + 1) = REAL(mo_set%maxocc, KIND=dp)
     503            0 :                   nomo = nomo + 1
     504            0 :                   irmo = CEILING(delectron/REAL(mo_set%maxocc, KIND=dp))
     505            0 :                   DO ir = 1, irmo
     506            0 :                      delectron = delectron - REAL(mo_set%maxocc, KIND=dp)
     507            0 :                      IF (delectron < 0.0_dp) THEN
     508            0 :                         mo_set%occupation_numbers(nomo + ir) = delectron + REAL(mo_set%maxocc, KIND=dp)
     509              :                      ELSE
     510            0 :                         mo_set%occupation_numbers(nomo + ir) = REAL(mo_set%maxocc, KIND=dp)
     511              :                      END IF
     512              :                   END DO
     513            0 :                   nomo = nomo + irmo
     514              :                ELSE
     515            0 :                   mo_set%occupation_numbers(nomo + 1) = total_zeff_corr
     516            0 :                   nomo = nomo + 1
     517              :                END IF
     518              :             END IF
     519              :          END IF
     520              :       END IF
     521       224420 :       nmo = SIZE(mo_set%eigenvalues)
     522              : 
     523       224420 :       CPASSERT(nmo >= nomo)
     524       224420 :       CPASSERT((SIZE(mo_set%occupation_numbers) == nmo))
     525              : 
     526       224420 :       mo_set%homo = nomo
     527       224420 :       mo_set%lfomo = nomo + 1
     528       224420 :       mo_set%mu = mo_set%eigenvalues(nomo)
     529              : 
     530              :       ! Check consistency of the array lengths
     531       224420 :       IF (PRESENT(eval_deriv)) THEN
     532            0 :          equal_size = (SIZE(mo_set%occupation_numbers, 1) == SIZE(eval_deriv, 1))
     533            0 :          CPASSERT(equal_size)
     534              :       END IF
     535              : 
     536              : !calling of HP module HERE, before smear
     537       224420 :       IF (PRESENT(probe)) THEN
     538           14 :          i_first = 1
     539           14 :          IF (smear%fixed_mag_mom == -1.0_dp) THEN
     540            0 :             nelec = REAL(mo_set%nelectron, dp)
     541              :          ELSE
     542           14 :             nelec = mo_set%n_el_f
     543              :          END IF
     544              : 
     545          294 :          mo_set%occupation_numbers(:) = 0.0_dp
     546              : 
     547              :          CALL probe_occupancy(mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, &
     548              :                               mo_set%eigenvalues, mo_set%mo_coeff, mo_set%maxocc, &
     549           14 :                               probe, N=nelec)
     550              :          !NB: mu and T are taken from the hairy_probe type (defined in cp_control_types.F); these values are set in the input
     551              : 
     552              :          ! Find the lowest fractional occupied MO (LFOMO)
     553          198 :          DO imo = i_first, nmo
     554          198 :             IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
     555           14 :                mo_set%lfomo = imo
     556           14 :                EXIT
     557              :             END IF
     558              :          END DO
     559          308 :          is_large = ABS(MAXVAL(mo_set%occupation_numbers) - mo_set%maxocc) > probe(1)%eps_hp
     560              :          ! this is not a real problem, but the temperature might be a bit large
     561           14 :          IF (is_large) &
     562            0 :             CPWARN("Hair-probes occupancy distribution includes the first MO")
     563              : 
     564              :          ! Find the highest (fractional) occupied MO which will be now the HOMO
     565           22 :          DO imo = nmo, mo_set%lfomo, -1
     566           22 :             IF (mo_set%occupation_numbers(imo) > probe(1)%eps_hp) THEN
     567           14 :                mo_set%homo = imo
     568           14 :                EXIT
     569              :             END IF
     570              :          END DO
     571          308 :          is_large = ABS(MINVAL(mo_set%occupation_numbers)) > probe(1)%eps_hp
     572           14 :          IF (is_large) &
     573              :             CALL cp_warn(__LOCATION__, &
     574              :                          "Hair-probes occupancy distribution includes the last MO => "// &
     575            6 :                          "Add more MOs for proper smearing.")
     576              : 
     577              :          ! check that the total electron count is accurate
     578           14 :          is_large = (ABS(nelec - accurate_sum(mo_set%occupation_numbers(:))) > probe(1)%eps_hp*nelec)
     579           14 :          IF (is_large) &
     580            0 :             CPWARN("Total number of electrons is not accurate")
     581              : 
     582              :       END IF
     583              : 
     584              :       ! Quick return, if no smearing information is supplied (TO BE FIXED, smear should become non-optional...)
     585       224420 :       IF (.NOT. PRESENT(smear)) THEN
     586              :          ! there is no dependence of the energy on the eigenvalues
     587          230 :          mo_set%uniform_occupation = .TRUE.
     588          230 :          IF (PRESENT(eval_deriv)) THEN
     589            0 :             eval_deriv = 0.0_dp
     590              :          END IF
     591          230 :          CALL timestop(handle)
     592          230 :          RETURN
     593              :       END IF
     594              : 
     595              :       ! Check if proper eigenvalues are already available
     596       224190 :       IF (smear%method /= smear_list) THEN
     597       224166 :          IF ((ABS(mo_set%eigenvalues(1)) < 1.0E-12_dp) .AND. &
     598              :              (ABS(mo_set%eigenvalues(nmo)) < 1.0E-12_dp)) THEN
     599        50884 :             CALL timestop(handle)
     600        50884 :             RETURN
     601              :          END IF
     602              :       END IF
     603              : 
     604              :       ! Perform smearing
     605       173306 :       IF (smear%do_smear) THEN
     606        19788 :          IF (PRESENT(xas_env)) THEN
     607           30 :             i_first = xas_estate + 1
     608           30 :             nelec = xas_nelectron
     609              :          ELSE
     610        19758 :             i_first = 1
     611        19758 :             IF (smear%fixed_mag_mom == -1.0_dp) THEN
     612            0 :                nelec = REAL(mo_set%nelectron, dp)
     613              :             ELSE
     614        19758 :                nelec = mo_set%n_el_f
     615              :             END IF
     616              :          END IF
     617        18220 :          SELECT CASE (smear%method)
     618              :          CASE (smear_fermi_dirac)
     619        18220 :             IF (.NOT. PRESENT(eval_deriv)) THEN
     620              :                CALL SmearFixed(mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, mo_set%kTS, &
     621              :                                mo_set%eigenvalues(1:mo_set%nmo), Nelec, &
     622              :                                smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac, &
     623        18220 :                                xas_estate, occ_estate)
     624              :             ELSE
     625            0 :                IF (.NOT. ALLOCATED(tmp_v)) ALLOCATE (tmp_v(SIZE(eval_deriv)))
     626            0 :                tmp_v(:) = eval_deriv - mo_set%eigenvalues + mo_set%mu
     627              :                CALL SmearFixedDerivMV(eval_deriv, mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, &
     628              :                                       mo_set%kTS, mo_set%eigenvalues(1:mo_set%nmo), Nelec, &
     629              :                                       smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac, &
     630            0 :                                       tmp_v, xas_estate, occ_estate)
     631              :             END IF
     632              : 
     633              :             ! Find the lowest fractional occupied MO (LFOMO)
     634       137354 :             DO imo = i_first, nmo
     635       137354 :                IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
     636        18220 :                   mo_set%lfomo = imo
     637        18220 :                   EXIT
     638              :                END IF
     639              :             END DO
     640       379230 :             is_large = ABS(MAXVAL(mo_set%occupation_numbers) - mo_set%maxocc) > smear%eps_fermi_dirac
     641              :             ! this is not a real problem, but the temperature might be a bit large
     642        18220 :             CPWARN_IF(is_large, "Fermi-Dirac smearing includes the first MO")
     643              : 
     644              :             ! Find the highest (fractional) occupied MO which will be now the HOMO
     645       194598 :             DO imo = nmo, mo_set%lfomo, -1
     646       194598 :                IF (mo_set%occupation_numbers(imo) > smear%eps_fermi_dirac) THEN
     647        10490 :                   mo_set%homo = imo
     648        10490 :                   EXIT
     649              :                END IF
     650              :             END DO
     651       379230 :             is_large = ABS(MINVAL(mo_set%occupation_numbers)) > smear%eps_fermi_dirac
     652        18220 :             IF (is_large) &
     653              :                CALL cp_warn(__LOCATION__, &
     654              :                             "Fermi-Dirac smearing includes the last MO => "// &
     655          684 :                             "Add more MOs for proper smearing.")
     656              : 
     657              :             ! check that the total electron count is accurate
     658        18220 :             is_large = (ABS(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
     659        18220 :             CPWARN_IF(is_large, "Total number of electrons is not accurate")
     660              : 
     661              :          CASE (smear_gaussian, smear_mp, smear_mv)
     662         1386 :             IF (.NOT. PRESENT(eval_deriv)) THEN
     663              :                CALL SmearFixed(mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, mo_set%kTS, &
     664              :                                mo_set%eigenvalues(1:mo_set%nmo), Nelec, &
     665              :                                smear%smearing_width, mo_set%maxocc, smear%method, &
     666         1386 :                                xas_estate, occ_estate)
     667              :             ELSE
     668            0 :                IF (.NOT. ALLOCATED(tmp_v)) ALLOCATE (tmp_v(SIZE(eval_deriv)))
     669            0 :                tmp_v(:) = eval_deriv - mo_set%eigenvalues + mo_set%mu
     670              :                CALL SmearFixedDerivMV(eval_deriv, mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, &
     671              :                                       mo_set%kTS, mo_set%eigenvalues(1:mo_set%nmo), Nelec, &
     672              :                                       smear%smearing_width, mo_set%maxocc, smear%method, &
     673            0 :                                       tmp_v, xas_estate, occ_estate)
     674              :             END IF
     675              : 
     676              :             ! Find the lowest fractional occupied MO (LFOMO)
     677        17480 :             DO imo = i_first, nmo
     678        17480 :                IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
     679         1386 :                   mo_set%lfomo = imo
     680         1386 :                   EXIT
     681              :                END IF
     682              :             END DO
     683        36212 :             is_large = ABS(MAXVAL(mo_set%occupation_numbers) - mo_set%maxocc) > smear%eps_fermi_dirac
     684         1386 :             CPWARN_IF(is_large, "Gaussian smearing includes the first MO")
     685              : 
     686              :             ! Find the highest (fractional) occupied MO which will be now the HOMO
     687        16852 :             DO imo = nmo, mo_set%lfomo, -1
     688        16852 :                IF (mo_set%occupation_numbers(imo) > smear%eps_fermi_dirac) THEN
     689         1014 :                   mo_set%homo = imo
     690         1014 :                   EXIT
     691              :                END IF
     692              :             END DO
     693        36212 :             is_large = ABS(MINVAL(mo_set%occupation_numbers)) > smear%eps_fermi_dirac
     694         1386 :             IF (is_large) &
     695              :                CALL cp_warn(__LOCATION__, &
     696              :                             "Gaussian smearing includes the last MO => "// &
     697            0 :                             "Add more MOs for proper smearing.")
     698              : 
     699              :             ! Check that the total electron count is accurate
     700         1386 :             is_large = (ABS(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
     701         1386 :             CPWARN_IF(is_large, "Total number of electrons is not accurate")
     702              : 
     703              :          CASE (smear_energy_window)
     704              :             ! not implemented
     705          158 :             CPASSERT(.NOT. PRESENT(eval_deriv))
     706              : 
     707              :             ! Define the energy window for the eigenvalues
     708          158 :             e1 = mo_set%eigenvalues(mo_set%homo) - 0.5_dp*smear%window_size
     709          158 :             IF (e1 <= mo_set%eigenvalues(1)) THEN
     710            0 :                CPWARN("Energy window for smearing includes the first MO")
     711              :             END IF
     712              : 
     713          158 :             e2 = mo_set%eigenvalues(mo_set%homo) + 0.5_dp*smear%window_size
     714          158 :             IF (e2 >= mo_set%eigenvalues(nmo)) &
     715              :                CALL cp_warn(__LOCATION__, &
     716              :                             "Energy window for smearing includes the last MO => "// &
     717            0 :                             "Add more MOs for proper smearing.")
     718              : 
     719              :             ! Find the lowest fractional occupied MO (LFOMO)
     720         2636 :             DO imo = i_first, nomo
     721         2636 :                IF (mo_set%eigenvalues(imo) > e1) THEN
     722          158 :                   mo_set%lfomo = imo
     723          158 :                   EXIT
     724              :                END IF
     725              :             END DO
     726              : 
     727              :             ! Find the highest fractional occupied (non-zero) MO which will be the HOMO
     728         1344 :             DO imo = nmo, nomo, -1
     729         1344 :                IF (mo_set%eigenvalues(imo) < e2) THEN
     730          158 :                   mo_set%homo = imo
     731          158 :                   EXIT
     732              :                END IF
     733              :             END DO
     734              : 
     735              :             ! Get the number of electrons to be smeared
     736          158 :             edist = 0.0_dp
     737          158 :             nelec = 0.0_dp
     738              : 
     739          390 :             DO imo = mo_set%lfomo, mo_set%homo
     740          232 :                nelec = nelec + mo_set%occupation_numbers(imo)
     741          390 :                edist = edist + ABS(e2 - mo_set%eigenvalues(imo))
     742              :             END DO
     743              : 
     744              :             ! Smear electrons inside the energy window
     745          390 :             DO imo = mo_set%lfomo, mo_set%homo
     746          232 :                edelta = ABS(e2 - mo_set%eigenvalues(imo))
     747          232 :                mo_set%occupation_numbers(imo) = MIN(mo_set%maxocc, nelec*edelta/edist)
     748          232 :                nelec = nelec - mo_set%occupation_numbers(imo)
     749          390 :                edist = edist - edelta
     750              :             END DO
     751              : 
     752              :          CASE (smear_list)
     753           24 :             equal_size = SIZE(mo_set%occupation_numbers, 1) == SIZE(smear%list, 1)
     754           24 :             CPASSERT(equal_size)
     755          168 :             mo_set%occupation_numbers = smear%list
     756              :             ! there is no dependence of the energy on the eigenvalues
     757           24 :             IF (PRESENT(eval_deriv)) THEN
     758            0 :                eval_deriv = 0.0_dp
     759              :             END IF
     760              :             ! most general case
     761           24 :             mo_set%lfomo = 1
     762        19812 :             mo_set%homo = nmo
     763              :          END SELECT
     764              : 
     765              :          ! Check, if the smearing involves more than one MO
     766        19788 :          IF (mo_set%lfomo == mo_set%homo) THEN
     767         1432 :             mo_set%homo = nomo
     768         1432 :             mo_set%lfomo = nomo + 1
     769              :          ELSE
     770        18356 :             mo_set%uniform_occupation = .FALSE.
     771              :          END IF
     772              : 
     773              :       END IF ! do smear
     774              : 
     775              :       ! zeros don't count as uniform
     776       173306 :       mo_set%uniform_occupation = has_uniform_occupation(mo_set=mo_set)
     777              : 
     778       173306 :       IF (ALLOCATED(tmp_v)) DEALLOCATE (tmp_v)
     779       173306 :       CALL timestop(handle)
     780              : 
     781       173306 :    END SUBROUTINE set_mo_occupation_1
     782              : 
     783              : END MODULE qs_mo_occupation
        

Generated by: LCOV version 2.0-1