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

Generated by: LCOV version 2.0-1