LCOV - code coverage report
Current view: top level - src - qs_kpoint_state.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 89.7 % 213 191
Test Date: 2026-10-15 00:55:44 Functions: 100.0 % 6 6

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief Methods for preparing and committing k-point orbital states to the QS environment.
      10              : ! **************************************************************************************************
      11              : MODULE qs_kpoint_state
      12              :    USE cp_cfm_basic_linalg,             ONLY: cp_cfm_scale,&
      13              :                                               cp_cfm_vectorsnorm
      14              :    USE cp_cfm_diag,                     ONLY: cp_cfm_geeig,&
      15              :                                               cp_cfm_heevd
      16              :    USE cp_cfm_types,                    ONLY: cp_cfm_create,&
      17              :                                               cp_cfm_release,&
      18              :                                               cp_cfm_set_all,&
      19              :                                               cp_cfm_to_cfm,&
      20              :                                               cp_cfm_type,&
      21              :                                               cp_fm_to_cfm
      22              :    USE cp_dbcsr_api,                    ONLY: dbcsr_p_type
      23              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      24              :                                               cp_fm_struct_release,&
      25              :                                               cp_fm_struct_type
      26              :    USE cp_fm_types,                     ONLY: cp_fm_get_info,&
      27              :                                               cp_fm_to_fm,&
      28              :                                               cp_fm_type,&
      29              :                                               cp_fm_vectorsnorm
      30              :    USE dft_plus_u,                      ONLY: lowdin_kp_apply
      31              :    USE kinds,                           ONLY: dp
      32              :    USE kpoint_methods,                  ONLY: kpoint_density_matrices,&
      33              :                                               kpoint_density_transform,&
      34              :                                               kpoint_set_mo_occupation
      35              :    USE kpoint_types,                    ONLY: get_kpoint_info,&
      36              :                                               kpoint_env_type,&
      37              :                                               kpoint_type
      38              :    USE mathconstants,                   ONLY: z_one,&
      39              :                                               z_zero
      40              :    USE message_passing,                 ONLY: mp_para_env_type
      41              :    USE parallel_gemm_api,               ONLY: parallel_gemm
      42              :    USE qs_energy_types,                 ONLY: qs_energy_type
      43              :    USE qs_environment_types,            ONLY: get_qs_env,&
      44              :                                               qs_environment_type
      45              :    USE qs_ks_types,                     ONLY: qs_ks_did_change
      46              :    USE qs_mo_types,                     ONLY: get_mo_set,&
      47              :                                               mo_set_type
      48              :    USE qs_neighbor_list_types,          ONLY: neighbor_list_set_p_type
      49              :    USE qs_rho_methods,                  ONLY: qs_rho_update_rho
      50              :    USE qs_rho_types,                    ONLY: qs_rho_get,&
      51              :                                               qs_rho_type
      52              :    USE qs_scf_types,                    ONLY: qs_scf_env_type
      53              :    USE scf_control_types,               ONLY: scf_control_type
      54              : #include "./base/base_uses.f90"
      55              : 
      56              :    IMPLICIT NONE
      57              :    PRIVATE
      58              : 
      59              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_kpoint_state'
      60              : 
      61              :    PUBLIC :: qs_kpoint_copy_spin_mos, qs_kpoint_mos_initialized, qs_kpoint_set_fixed_occupations, &
      62              :              qs_kpoint_state_canonicalize_fixed, qs_kpoint_state_commit, &
      63              :              qs_kpoint_state_prepare_fixed_density
      64              : 
      65              : CONTAINS
      66              : 
      67              : ! **************************************************************************************************
      68              : !> \brief Reconstructs fixed-rank complex k-point orbitals from a density kernel.
      69              : !>        The density and overlap must first be transferred to kp%ot_hmat and kp%ot_smat. The
      70              : !>        largest natural-orbital subspace gives a metric-defined fixed-rank projector without
      71              : !>        replacing the guess by the ground-state projector of its initial Hamiltonian.
      72              : !> \param kpoints K-point environment containing the transferred density and overlap matrices.
      73              : ! **************************************************************************************************
      74           58 :    SUBROUTINE qs_kpoint_state_prepare_fixed_density(kpoints)
      75              :       TYPE(kpoint_type), POINTER                         :: kpoints
      76              : 
      77              :       INTEGER                                            :: ikpoint_local, ispin, nao, nmo, nocc
      78              :       LOGICAL                                            :: use_real_wfn
      79           58 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: density_eigenvalues
      80              :       TYPE(cp_cfm_type)                                  :: density_metric, density_work, matrix_p, &
      81              :                                                             matrix_s, matrix_s_work, projected
      82              :       TYPE(cp_cfm_type), POINTER                         :: cmo_coeff
      83              :       TYPE(cp_fm_struct_type), POINTER                   :: active_struct, square_struct
      84              :       TYPE(kpoint_env_type), POINTER                     :: kp
      85              : 
      86           58 :       NULLIFY (active_struct, cmo_coeff, kp, square_struct)
      87              : 
      88            0 :       CPASSERT(ASSOCIATED(kpoints))
      89           58 :       CPASSERT(ASSOCIATED(kpoints%kp_env))
      90           58 :       CALL get_kpoint_info(kpoints, use_real_wfn=use_real_wfn)
      91           58 :       IF (use_real_wfn) THEN
      92            0 :          CPABORT("Fixed-density k-point OT initialization requires complex wavefunctions.")
      93              :       END IF
      94              : 
      95          189 :       DO ikpoint_local = 1, SIZE(kpoints%kp_env)
      96          131 :          kp => kpoints%kp_env(ikpoint_local)%kpoint_env
      97          131 :          CPASSERT(ASSOCIATED(kp))
      98          131 :          CPASSERT(ASSOCIATED(kp%mos))
      99          131 :          CPASSERT(ASSOCIATED(kp%ot_hmat))
     100          131 :          CPASSERT(ASSOCIATED(kp%ot_smat))
     101          131 :          CPASSERT(SIZE(kp%ot_hmat, 1) >= 2)
     102          131 :          CPASSERT(SIZE(kp%ot_hmat, 2) >= SIZE(kp%mos))
     103          131 :          CPASSERT(SIZE(kp%ot_smat) >= 2)
     104              : 
     105          131 :          CALL cp_fm_get_info(kp%ot_hmat(1, 1), matrix_struct=square_struct, nrow_global=nao)
     106          131 :          CALL cp_cfm_create(matrix_p, square_struct, name="natural-orbital density P(k)")
     107          131 :          CALL cp_cfm_create(matrix_s, square_struct, name="natural-orbital overlap S(k)")
     108          131 :          CALL cp_fm_to_cfm(kp%ot_smat(1), kp%ot_smat(2), matrix_s)
     109              : 
     110          293 :          DO ispin = 1, SIZE(kp%mos)
     111          162 :             CALL get_mo_set(kp%mos(ispin), homo=nocc, cmo_coeff=cmo_coeff, nmo=nmo)
     112          162 :             CPASSERT(ASSOCIATED(cmo_coeff))
     113          162 :             CPASSERT(nocc >= 1 .AND. nocc <= nmo)
     114              : 
     115              :             CALL cp_fm_struct_create(active_struct, template_fmstruct=cmo_coeff%matrix_struct, &
     116          162 :                                      ncol_global=nocc)
     117          162 :             CALL cp_fm_to_cfm(kp%ot_hmat(1, ispin), kp%ot_hmat(2, ispin), matrix_p)
     118          162 :             CALL cp_cfm_create(projected, active_struct, name="fixed-density natural orbitals")
     119          162 :             CALL cp_cfm_create(density_metric, square_struct, name="natural-orbital metric")
     120          162 :             CALL cp_cfm_create(density_work, square_struct, name="natural-orbital work")
     121          162 :             CALL cp_cfm_create(matrix_s_work, square_struct, name="natural-orbital overlap work")
     122              : 
     123              :             ! The largest eigenvectors of S*P*S*C = S*C*n are the natural orbitals of
     124              :             ! S**(1/2)*P*S**(1/2). Negating the left-hand side lets the generalized
     125              :             ! eigensolver request only its lowest nocc eigenpairs when supported.
     126              :             CALL parallel_gemm('N', 'N', nao, nao, nao, z_one, matrix_p, matrix_s, &
     127          162 :                                z_zero, density_work)
     128              :             CALL parallel_gemm('N', 'N', nao, nao, nao, z_one, matrix_s, density_work, &
     129          162 :                                z_zero, density_metric)
     130          162 :             CALL cp_cfm_scale(-z_one, density_metric)
     131          162 :             CALL cp_cfm_to_cfm(matrix_s, matrix_s_work)
     132          486 :             ALLOCATE (density_eigenvalues(nocc))
     133              :             CALL cp_cfm_geeig(density_metric, matrix_s_work, projected, &
     134          162 :                               density_eigenvalues, density_work, lowest_subset=.TRUE.)
     135          162 :             DEALLOCATE (density_eigenvalues)
     136              : 
     137          162 :             CALL cp_cfm_set_all(cmo_coeff, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
     138          162 :             CALL cp_cfm_to_cfm(projected, cmo_coeff, nocc)
     139          162 :             CALL cp_cfm_release(matrix_s_work)
     140          162 :             CALL cp_cfm_release(density_work)
     141          162 :             CALL cp_cfm_release(density_metric)
     142          162 :             CALL cp_cfm_release(projected)
     143          617 :             CALL cp_fm_struct_release(active_struct)
     144              :          END DO
     145              : 
     146          131 :          CALL cp_cfm_release(matrix_s)
     147          320 :          CALL cp_cfm_release(matrix_p)
     148              :       END DO
     149              : 
     150          116 :    END SUBROUTINE qs_kpoint_state_prepare_fixed_density
     151              : 
     152              : ! **************************************************************************************************
     153              : !> \brief Rotates a uniformly occupied k-point subspace into Ritz states of the current
     154              : !>        Hamiltonian and stores their energy labels. The Hamiltonian must first be transferred
     155              : !>        to kp%ot_hmat. Since the rotation is internal to a uniformly occupied subspace, it does
     156              : !>        not change the density represented by the orbitals.
     157              : !> \param kpoints K-point environment containing the orbitals and transferred Hamiltonians.
     158              : ! **************************************************************************************************
     159           58 :    SUBROUTINE qs_kpoint_state_canonicalize_fixed(kpoints)
     160              :       TYPE(kpoint_type), POINTER                         :: kpoints
     161              : 
     162              :       INTEGER                                            :: ikpoint_local, ispin, nao, nmo, nocc
     163              :       LOGICAL                                            :: use_real_wfn
     164           58 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: ritz_values
     165           58 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: eigenvalues_re
     166              :       TYPE(cp_cfm_type)                                  :: coeff, eigenvectors, hblock, hc, &
     167              :                                                             matrix_h, rotated
     168              :       TYPE(cp_cfm_type), POINTER                         :: cmo_coeff
     169              :       TYPE(cp_fm_struct_type), POINTER                   :: active_struct, square_struct
     170              :       TYPE(kpoint_env_type), POINTER                     :: kp
     171              : 
     172           58 :       NULLIFY (active_struct, cmo_coeff, eigenvalues_re, kp, &
     173           58 :                square_struct)
     174              : 
     175            0 :       CPASSERT(ASSOCIATED(kpoints))
     176           58 :       CPASSERT(ASSOCIATED(kpoints%kp_env))
     177           58 :       CALL get_kpoint_info(kpoints, use_real_wfn=use_real_wfn)
     178           58 :       IF (use_real_wfn) THEN
     179            0 :          CPABORT("Fixed-density k-point OT canonicalization requires complex wavefunctions.")
     180              :       END IF
     181              : 
     182          189 :       DO ikpoint_local = 1, SIZE(kpoints%kp_env)
     183          131 :          kp => kpoints%kp_env(ikpoint_local)%kpoint_env
     184          131 :          CPASSERT(ASSOCIATED(kp))
     185          131 :          CPASSERT(ASSOCIATED(kp%mos))
     186          131 :          CPASSERT(ASSOCIATED(kp%ot_hmat))
     187          131 :          CPASSERT(SIZE(kp%ot_hmat, 1) >= 2)
     188          131 :          CPASSERT(SIZE(kp%ot_hmat, 2) >= SIZE(kp%mos))
     189              : 
     190          131 :          CALL cp_fm_get_info(kp%ot_hmat(1, 1), matrix_struct=square_struct, nrow_global=nao)
     191          131 :          CALL cp_cfm_create(matrix_h, square_struct, name="fixed-density Ritz Hamiltonian")
     192              : 
     193          293 :          DO ispin = 1, SIZE(kp%mos)
     194              :             CALL get_mo_set(kp%mos(ispin), homo=nocc, cmo_coeff=cmo_coeff, nmo=nmo, &
     195          162 :                             eigenvalues=eigenvalues_re)
     196          162 :             CPASSERT(ASSOCIATED(cmo_coeff))
     197          162 :             CPASSERT(ASSOCIATED(eigenvalues_re))
     198          162 :             CPASSERT(nocc >= 1 .AND. nocc <= nmo)
     199          162 :             CPASSERT(SIZE(eigenvalues_re) >= nocc)
     200              : 
     201              :             CALL cp_fm_struct_create(active_struct, template_fmstruct=cmo_coeff%matrix_struct, &
     202          162 :                                      ncol_global=nocc)
     203          162 :             CALL cp_cfm_create(coeff, active_struct, name="fixed-density Ritz orbitals")
     204          162 :             CALL cp_cfm_create(hc, active_struct, name="fixed-density Ritz HC")
     205          162 :             CALL cp_cfm_create(rotated, active_struct, name="fixed-density Ritz rotated orbitals")
     206              :             CALL cp_cfm_create(hblock, active_struct, nrow=nocc, ncol=nocc, &
     207          162 :                                name="fixed-density projected Hamiltonian")
     208              :             CALL cp_cfm_create(eigenvectors, active_struct, nrow=nocc, ncol=nocc, &
     209          162 :                                name="fixed-density Ritz eigenvectors")
     210          162 :             CALL cp_fm_to_cfm(kp%ot_hmat(1, ispin), kp%ot_hmat(2, ispin), matrix_h)
     211          162 :             IF (ALLOCATED(kpoints%lowdin_v)) CALL lowdin_kp_apply(kp, kpoints%lowdin_v(:, ispin), cmat=matrix_h)
     212          162 :             CALL cp_cfm_to_cfm(cmo_coeff, coeff, nocc)
     213              : 
     214          162 :             CALL parallel_gemm('N', 'N', nao, nocc, nao, z_one, matrix_h, coeff, z_zero, hc)
     215          162 :             CALL parallel_gemm('C', 'N', nocc, nocc, nao, z_one, coeff, hc, z_zero, hblock)
     216          486 :             ALLOCATE (ritz_values(nocc))
     217          162 :             CALL cp_cfm_heevd(hblock, eigenvectors, ritz_values)
     218              :             CALL parallel_gemm('N', 'N', nao, nocc, nocc, z_one, coeff, eigenvectors, &
     219          162 :                                z_zero, rotated)
     220          162 :             CALL cp_cfm_to_cfm(rotated, cmo_coeff, nocc)
     221          663 :             eigenvalues_re(1:nocc) = ritz_values
     222          162 :             DEALLOCATE (ritz_values)
     223              : 
     224          162 :             CALL cp_cfm_release(eigenvectors)
     225          162 :             CALL cp_cfm_release(hblock)
     226          162 :             CALL cp_cfm_release(rotated)
     227          162 :             CALL cp_cfm_release(hc)
     228          162 :             CALL cp_cfm_release(coeff)
     229          617 :             CALL cp_fm_struct_release(active_struct)
     230              :          END DO
     231              : 
     232          320 :          CALL cp_cfm_release(matrix_h)
     233              :       END DO
     234              : 
     235          116 :    END SUBROUTINE qs_kpoint_state_canonicalize_fixed
     236              : 
     237              : ! **************************************************************************************************
     238              : !> \brief Rebuilds the physical density from the current k-point orbitals and occupations.
     239              : !> \param qs_env QS environment containing the orbital state to commit.
     240              : !> \param update_occupations Recompute occupations from the current MO energies and SCF settings.
     241              : !> \param separate_spin_occupations Fill restricted energy channels independently.
     242              : !> \param fixed_occupations Restore the fixed occupied rank required by OT.
     243              : ! **************************************************************************************************
     244         1048 :    SUBROUTINE qs_kpoint_state_commit(qs_env, update_occupations, &
     245              :                                      separate_spin_occupations, fixed_occupations)
     246              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     247              :       LOGICAL, INTENT(IN)                                :: update_occupations
     248              :       LOGICAL, INTENT(IN), OPTIONAL                      :: separate_spin_occupations, &
     249              :                                                             fixed_occupations
     250              : 
     251              :       INTEGER                                            :: ispin
     252              :       LOGICAL                                            :: fixed, separate_spins
     253         1048 :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: matrix_s_kp, rho_ao_kp
     254              :       TYPE(kpoint_type), POINTER                         :: kpoints
     255         1048 :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos
     256              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     257         1048 :          POINTER                                         :: sab_nl
     258              :       TYPE(qs_energy_type), POINTER                      :: energy
     259              :       TYPE(qs_rho_type), POINTER                         :: rho
     260              :       TYPE(qs_scf_env_type), POINTER                     :: scf_env
     261              :       TYPE(scf_control_type), POINTER                    :: scf_control
     262              : 
     263         1048 :       NULLIFY (energy, kpoints, matrix_s_kp, mos, rho_ao_kp, sab_nl, rho, scf_env, scf_control)
     264              : 
     265         1048 :       fixed = .FALSE.
     266         1048 :       separate_spins = .FALSE.
     267         1040 :       IF (PRESENT(fixed_occupations)) fixed = fixed_occupations
     268         1048 :       IF (PRESENT(separate_spin_occupations)) separate_spins = separate_spin_occupations
     269         1048 :       IF (fixed .AND. .NOT. update_occupations) THEN
     270            0 :          CPABORT("Fixed occupations can only be requested while updating occupations.")
     271              :       END IF
     272              : 
     273              :       CALL get_qs_env(qs_env, kpoints=kpoints, matrix_s_kp=matrix_s_kp, &
     274         1048 :                       energy=energy, rho=rho, scf_env=scf_env, scf_control=scf_control)
     275         1048 :       CALL get_kpoint_info(kpoints, sab_nl=sab_nl)
     276              : 
     277         1048 :       IF (update_occupations) THEN
     278              :          CALL kpoint_set_mo_occupation( &
     279         1040 :             kpoints, scf_control%smear, separate_spin_occupations=separate_spins)
     280         1040 :          IF (fixed) CALL qs_kpoint_set_fixed_occupations(kpoints)
     281              :       END IF
     282         1048 :       CALL kpoint_density_matrices(kpoints)
     283         1048 :       CALL qs_rho_get(rho, rho_ao_kp=rho_ao_kp)
     284              :       CALL kpoint_density_transform(kpoints, rho_ao_kp, .FALSE., &
     285              :                                     matrix_s_kp(1, 1)%matrix, sab_nl, scf_env%scf_work1, &
     286         1048 :                                     overlap_rs=matrix_s_kp)
     287         1048 :       CALL qs_rho_update_rho(rho, qs_env=qs_env)
     288         1048 :       CALL qs_ks_did_change(qs_env%ks_env, rho_changed=.TRUE.)
     289              : 
     290         1048 :       energy%kTS = 0.0_dp
     291         1048 :       energy%efermi = 0.0_dp
     292         1048 :       mos => kpoints%kp_env(1)%kpoint_env%mos
     293         1048 :       CPASSERT(ASSOCIATED(mos))
     294         2286 :       DO ispin = 1, SIZE(mos)
     295         1238 :          energy%kTS = energy%kTS + mos(ispin)%kTS
     296         2286 :          energy%efermi = energy%efermi + mos(ispin)%mu
     297              :       END DO
     298         1048 :       energy%efermi = energy%efermi/REAL(SIZE(mos), KIND=dp)
     299              : 
     300         1048 :    END SUBROUTINE qs_kpoint_state_commit
     301              : 
     302              : ! **************************************************************************************************
     303              : !> \brief Checks whether every local k-point channel contains nonzero occupied orbitals.
     304              : !> \param kpoints K-point environment.
     305              : !> \param require_full_space Require every available orbital instead of the occupied block.
     306              : !> \return True if every channel contains a nonzero real or imaginary coefficient.
     307              : ! **************************************************************************************************
     308          274 :    FUNCTION qs_kpoint_mos_initialized(kpoints, require_full_space) RESULT(initialized)
     309              :       TYPE(kpoint_type), POINTER                         :: kpoints
     310              :       LOGICAL, INTENT(IN), OPTIONAL                      :: require_full_space
     311              :       LOGICAL                                            :: initialized
     312              : 
     313              :       INTEGER                                            :: ikpoint_local, ispin, nactive, nmo, nocc
     314              :       LOGICAL                                            :: full_space
     315              :       REAL(KIND=dp)                                      :: coefficient_norm
     316          274 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: column_norm
     317              :       TYPE(cp_cfm_type), POINTER                         :: cmo_coeff
     318              :       TYPE(cp_fm_type), POINTER                          :: mo_coeff
     319              :       TYPE(kpoint_env_type), POINTER                     :: kp
     320              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     321              : 
     322          274 :       NULLIFY (cmo_coeff, kp, mo_coeff, para_env)
     323          274 :       CPASSERT(ASSOCIATED(kpoints))
     324          274 :       CPASSERT(ASSOCIATED(kpoints%kp_env))
     325              : 
     326          274 :       full_space = .FALSE.
     327          274 :       IF (PRESENT(require_full_space)) full_space = require_full_space
     328          274 :       coefficient_norm = HUGE(1.0_dp)
     329          787 :       DO ikpoint_local = 1, SIZE(kpoints%kp_env)
     330          513 :          kp => kpoints%kp_env(ikpoint_local)%kpoint_env
     331          513 :          CPASSERT(ASSOCIATED(kp))
     332          513 :          CPASSERT(ASSOCIATED(kp%mos))
     333         1385 :          DO ispin = 1, SIZE(kp%mos)
     334              :             CALL get_mo_set(kp%mos(ispin), homo=nocc, cmo_coeff=cmo_coeff, &
     335          598 :                             mo_coeff=mo_coeff, nmo=nmo)
     336          598 :             nactive = MERGE(nmo, nocc, full_space)
     337          598 :             IF (nactive < 1 .OR. nactive > nmo) THEN
     338            0 :                coefficient_norm = 0.0_dp
     339            0 :                CYCLE
     340              :             END IF
     341         1794 :             ALLOCATE (column_norm(nmo))
     342          598 :             IF (ASSOCIATED(cmo_coeff)) THEN
     343          598 :                CALL cp_cfm_vectorsnorm(cmo_coeff, column_norm)
     344              :             ELSE
     345            0 :                CPASSERT(ASSOCIATED(mo_coeff))
     346            0 :                CALL cp_fm_vectorsnorm(mo_coeff, column_norm)
     347              :             END IF
     348         3489 :             coefficient_norm = MIN(coefficient_norm, MINVAL(column_norm(1:nactive)))
     349         1709 :             DEALLOCATE (column_norm)
     350              :          END DO
     351              :       END DO
     352              : 
     353          274 :       CALL get_kpoint_info(kpoints, para_env=para_env)
     354          274 :       CPASSERT(ASSOCIATED(para_env))
     355          274 :       CALL para_env%min(coefficient_norm)
     356          274 :       initialized = coefficient_norm > 100.0_dp*EPSILON(1.0_dp)
     357              : 
     358          274 :    END FUNCTION qs_kpoint_mos_initialized
     359              : 
     360              : ! **************************************************************************************************
     361              : !> \brief Copies the first k-point spin channel into the remaining channels.
     362              : !> \param kpoints K-point environment.
     363              : !> \param nspin Number of spin channels to populate.
     364              : ! **************************************************************************************************
     365           10 :    SUBROUTINE qs_kpoint_copy_spin_mos(kpoints, nspin)
     366              :       TYPE(kpoint_type), POINTER                         :: kpoints
     367              :       INTEGER, INTENT(IN)                                :: nspin
     368              : 
     369              :       INTEGER                                            :: ikpoint_local, ispin
     370           10 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: eigenvalues_source, eigenvalues_target
     371              :       TYPE(cp_cfm_type), POINTER                         :: cmo_coeff_source, cmo_coeff_target
     372              :       TYPE(cp_fm_type), POINTER                          :: mo_coeff_source, mo_coeff_target
     373              :       TYPE(kpoint_env_type), POINTER                     :: kp
     374              : 
     375           10 :       NULLIFY (cmo_coeff_source, cmo_coeff_target, eigenvalues_source, eigenvalues_target, kp, &
     376           10 :                mo_coeff_source, mo_coeff_target)
     377            0 :       CPASSERT(ASSOCIATED(kpoints))
     378           10 :       CPASSERT(ASSOCIATED(kpoints%kp_env))
     379           10 :       CPASSERT(nspin >= 1)
     380              : 
     381           21 :       DO ikpoint_local = 1, SIZE(kpoints%kp_env)
     382           11 :          kp => kpoints%kp_env(ikpoint_local)%kpoint_env
     383           11 :          CPASSERT(ASSOCIATED(kp))
     384           11 :          CPASSERT(ASSOCIATED(kp%mos))
     385           11 :          CPASSERT(SIZE(kp%mos) >= nspin)
     386           21 :          IF (ASSOCIATED(kp%mos(1)%cmo_coeff)) THEN
     387           11 :             cmo_coeff_source => kp%mos(1)%cmo_coeff
     388           11 :             CALL get_mo_set(kp%mos(1), eigenvalues=eigenvalues_source)
     389           11 :             CPASSERT(ASSOCIATED(eigenvalues_source))
     390           22 :             DO ispin = 2, nspin
     391           11 :                cmo_coeff_target => kp%mos(ispin)%cmo_coeff
     392           11 :                CALL get_mo_set(kp%mos(ispin), eigenvalues=eigenvalues_target)
     393           11 :                CPASSERT(ASSOCIATED(cmo_coeff_target))
     394           11 :                CPASSERT(ASSOCIATED(eigenvalues_target))
     395           11 :                CALL cp_cfm_to_cfm(cmo_coeff_source, cmo_coeff_target)
     396          173 :                eigenvalues_target(:) = eigenvalues_source(:)
     397              :             END DO
     398              :          ELSE
     399            0 :             CALL get_mo_set(kp%mos(1), mo_coeff=mo_coeff_source, eigenvalues=eigenvalues_source)
     400            0 :             CPASSERT(ASSOCIATED(mo_coeff_source))
     401            0 :             CPASSERT(ASSOCIATED(eigenvalues_source))
     402            0 :             DO ispin = 2, nspin
     403              :                CALL get_mo_set(kp%mos(ispin), mo_coeff=mo_coeff_target, &
     404            0 :                                eigenvalues=eigenvalues_target)
     405            0 :                CPASSERT(ASSOCIATED(mo_coeff_target))
     406            0 :                CPASSERT(ASSOCIATED(eigenvalues_target))
     407            0 :                CALL cp_fm_to_fm(mo_coeff_source, mo_coeff_target)
     408            0 :                eigenvalues_target(:) = eigenvalues_source(:)
     409              :             END DO
     410              :          END IF
     411              :       END DO
     412              : 
     413           10 :    END SUBROUTINE qs_kpoint_copy_spin_mos
     414              : 
     415              : ! **************************************************************************************************
     416              : !> \brief Restores the fixed occupied rank required by k-point OT.
     417              : !> \param kpoints K-point environment.
     418              : ! **************************************************************************************************
     419          508 :    SUBROUTINE qs_kpoint_set_fixed_occupations(kpoints)
     420              :       TYPE(kpoint_type), POINTER                         :: kpoints
     421              : 
     422              :       INTEGER                                            :: ikpoint_local, ispin, nelectron, nmo, &
     423              :                                                             nocc
     424              :       REAL(KIND=dp)                                      :: maxocc
     425          508 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: occupation_numbers
     426              :       TYPE(kpoint_env_type), POINTER                     :: kp
     427              : 
     428          508 :       NULLIFY (kp, occupation_numbers)
     429            0 :       CPASSERT(ASSOCIATED(kpoints))
     430          508 :       CPASSERT(ASSOCIATED(kpoints%kp_env))
     431              : 
     432         1538 :       DO ikpoint_local = 1, SIZE(kpoints%kp_env)
     433         1030 :          kp => kpoints%kp_env(ikpoint_local)%kpoint_env
     434         1030 :          CPASSERT(ASSOCIATED(kp))
     435         1030 :          CPASSERT(ASSOCIATED(kp%mos))
     436         2722 :          DO ispin = 1, SIZE(kp%mos)
     437              :             CALL get_mo_set(kp%mos(ispin), maxocc=maxocc, nelectron=nelectron, &
     438         1184 :                             nmo=nmo, occupation_numbers=occupation_numbers)
     439         1184 :             CPASSERT(ASSOCIATED(occupation_numbers))
     440              :             ! The fixed rank comes from the spin electron count, not a previous k-point filling.
     441         1184 :             nocc = NINT(REAL(nelectron, KIND=dp)/maxocc)
     442         1184 :             IF (nocc < 1 .OR. nocc > nmo) THEN
     443            0 :                CPABORT("Fixed-occupation k-point OT requires a nonempty occupied subspace.")
     444              :             END IF
     445         1184 :             IF (ABS(maxocc*REAL(nocc, KIND=dp) - REAL(nelectron, KIND=dp)) > &
     446              :                 100.0_dp*EPSILON(1.0_dp)) THEN
     447            0 :                CPABORT("Fixed-occupation k-point OT requires uniform integer occupations.")
     448              :             END IF
     449         5952 :             occupation_numbers(:) = 0.0_dp
     450         5902 :             occupation_numbers(1:nocc) = maxocc
     451         1184 :             kp%mos(ispin)%homo = nocc
     452         3398 :             kp%mos(ispin)%kTS = 0.0_dp
     453              :          END DO
     454              :       END DO
     455              : 
     456          508 :    END SUBROUTINE qs_kpoint_set_fixed_occupations
     457              : 
     458              : END MODULE qs_kpoint_state
        

Generated by: LCOV version 2.0-1