LCOV - code coverage report
Current view: top level - src - qs_energy_window.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:2fce0f8) Lines: 117 118 99.2 %
Date: 2024-12-21 06:28:57 Functions: 1 1 100.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief Does all kind of post scf calculations for GPW/GAPW
      10             : !> \par History
      11             : !>      Started as a copy from the relevant part of qs_scf
      12             : !>      Start to adapt for k-points [07.2015, JGH]
      13             : !> \author Joost VandeVondele (10.2003)
      14             : ! **************************************************************************************************
      15             : MODULE qs_energy_window
      16             :    USE cp_blacs_env,                    ONLY: cp_blacs_env_type
      17             :    USE cp_dbcsr_api,                    ONLY: &
      18             :         dbcsr_add, dbcsr_copy, dbcsr_create, dbcsr_desymmetrize, dbcsr_frobenius_norm, &
      19             :         dbcsr_get_info, dbcsr_multiply, dbcsr_p_type, dbcsr_release, dbcsr_type
      20             :    USE cp_dbcsr_operations,             ONLY: copy_dbcsr_to_fm,&
      21             :                                               copy_fm_to_dbcsr
      22             :    USE cp_fm_basic_linalg,              ONLY: cp_fm_trace
      23             :    USE cp_fm_diag,                      ONLY: choose_eigv_solver
      24             :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      25             :                                               cp_fm_struct_release,&
      26             :                                               cp_fm_struct_type
      27             :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      28             :                                               cp_fm_release,&
      29             :                                               cp_fm_to_fm,&
      30             :                                               cp_fm_type
      31             :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      32             :                                               cp_logger_get_default_io_unit,&
      33             :                                               cp_logger_type
      34             :    USE cp_output_handling,              ONLY: cp_iter_string,&
      35             :                                               cp_print_key_finished_output,&
      36             :                                               cp_print_key_unit_nr
      37             :    USE cp_realspace_grid_cube,          ONLY: cp_pw_to_cube
      38             :    USE input_section_types,             ONLY: section_get_ivals,&
      39             :                                               section_vals_get_subs_vals,&
      40             :                                               section_vals_type,&
      41             :                                               section_vals_val_get
      42             :    USE iterate_matrix,                  ONLY: matrix_sqrt_Newton_Schulz
      43             :    USE kinds,                           ONLY: dp
      44             :    USE parallel_gemm_api,               ONLY: parallel_gemm
      45             :    USE particle_list_types,             ONLY: particle_list_type
      46             :    USE pw_env_types,                    ONLY: pw_env_get,&
      47             :                                               pw_env_type
      48             :    USE pw_methods,                      ONLY: pw_integrate_function
      49             :    USE pw_pool_types,                   ONLY: pw_pool_type
      50             :    USE pw_types,                        ONLY: pw_c1d_gs_type,&
      51             :                                               pw_r3d_rs_type
      52             :    USE qs_collocate_density,            ONLY: calculate_rho_elec
      53             :    USE qs_environment_types,            ONLY: get_qs_env,&
      54             :                                               qs_environment_type
      55             :    USE qs_ks_types,                     ONLY: qs_ks_env_type
      56             :    USE qs_rho_types,                    ONLY: qs_rho_get,&
      57             :                                               qs_rho_type
      58             :    USE qs_subsys_types,                 ONLY: qs_subsys_get,&
      59             :                                               qs_subsys_type
      60             : #include "./base/base_uses.f90"
      61             : 
      62             :    IMPLICIT NONE
      63             :    PRIVATE
      64             : 
      65             :    ! Global parameters
      66             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_energy_window'
      67             : 
      68             :    PUBLIC :: energy_windows
      69             : 
      70             : ! **************************************************************************************************
      71             : 
      72             : CONTAINS
      73             : 
      74             : ! **************************************************************************************************
      75             : !> \brief ...
      76             : !> \param qs_env ...
      77             : ! **************************************************************************************************
      78          92 :    SUBROUTINE energy_windows(qs_env)
      79             : 
      80             :       TYPE(qs_environment_type), POINTER                 :: qs_env
      81             : 
      82             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'energy_windows'
      83             :       LOGICAL, PARAMETER                                 :: debug = .FALSE.
      84             :       REAL(KIND=dp), PARAMETER                           :: one = 1.0_dp, zero = 0.0_dp
      85             : 
      86             :       CHARACTER(len=40)                                  :: ext, title
      87             :       INTEGER                                            :: handle, i, lanzcos_max_iter, last, nao, &
      88             :                                                             nelectron_total, newton_schulz_order, &
      89             :                                                             next, nwindows, print_unit, unit_nr
      90          92 :       INTEGER, DIMENSION(:), POINTER                     :: stride(:)
      91             :       LOGICAL                                            :: mpi_io, print_cube, restrict_range
      92             :       REAL(KIND=dp) :: bin_width, density_ewindow_total, density_total, energy_range, fermi_level, &
      93             :          filter_eps, frob_norm, lanzcos_threshold, lower_bound, occupation, upper_bound
      94          92 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues, P_eigenvalues, &
      95          92 :                                                             window_eigenvalues
      96             :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
      97             :       TYPE(cp_fm_struct_type), POINTER                   :: ao_ao_fmstruct, window_fm_struct
      98             :       TYPE(cp_fm_type) :: eigenvectors, eigenvectors_nonorth, matrix_ks_fm, P_eigenvectors, &
      99             :          P_window_fm, rho_ao_ortho_fm, S_minus_half_fm, tmp_fm, window_eigenvectors, window_fm
     100             :       TYPE(cp_logger_type), POINTER                      :: logger
     101          92 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_ks, matrix_s, rho_ao
     102             :       TYPE(dbcsr_type)                                   :: matrix_ks_nosym, S_half, S_minus_half, &
     103             :                                                             tmp
     104             :       TYPE(dbcsr_type), POINTER                          :: rho_ao_ortho, window
     105             :       TYPE(particle_list_type), POINTER                  :: particles
     106             :       TYPE(pw_c1d_gs_type)                               :: rho_g
     107             :       TYPE(pw_env_type), POINTER                         :: pw_env
     108             :       TYPE(pw_pool_type), POINTER                        :: auxbas_pw_pool
     109             :       TYPE(pw_r3d_rs_type)                               :: rho_r
     110             :       TYPE(qs_ks_env_type), POINTER                      :: ks_env
     111             :       TYPE(qs_rho_type), POINTER                         :: rho
     112             :       TYPE(qs_subsys_type), POINTER                      :: subsys
     113             :       TYPE(section_vals_type), POINTER                   :: dft_section, input, ls_scf_section
     114             : 
     115          92 :       CALL timeset(routineN, handle)
     116             : 
     117          92 :       logger => cp_get_default_logger()
     118          92 :       unit_nr = cp_logger_get_default_io_unit(logger)
     119             :       CALL get_qs_env(qs_env=qs_env, blacs_env=blacs_env, matrix_ks=matrix_ks, pw_env=pw_env, rho=rho, &
     120          92 :                       input=input, nelectron_total=nelectron_total, subsys=subsys, ks_env=ks_env, matrix_s=matrix_s)
     121          92 :       CALL qs_subsys_get(subsys, particles=particles)
     122          92 :       CALL qs_rho_get(rho_struct=rho, rho_ao=rho_ao)
     123          92 :       IF (SIZE(rho_ao) > 1) CALL cp_warn(__LOCATION__, &
     124           0 :                                          "The printing of energy windows is currently only implemented for clsoe shell systems")
     125          92 :       CALL pw_env_get(pw_env, auxbas_pw_pool=auxbas_pw_pool)
     126             : 
     127             :       !reading the input
     128          92 :       dft_section => section_vals_get_subs_vals(input, "DFT")
     129          92 :       ls_scf_section => section_vals_get_subs_vals(input, "DFT%LS_SCF")
     130          92 :       CALL section_vals_val_get(dft_section, "PRINT%ENERGY_WINDOWS%N_WINDOWS", i_val=nwindows)
     131          92 :       CALL section_vals_val_get(dft_section, "PRINT%ENERGY_WINDOWS%PRINT_CUBES", l_val=print_cube)
     132          92 :       CALL section_vals_val_get(dft_section, "PRINT%ENERGY_WINDOWS%RESTRICT_RANGE", l_val=restrict_range)
     133          92 :       CALL section_vals_val_get(dft_section, "PRINT%ENERGY_WINDOWS%RANGE", r_val=energy_range)
     134             :       NULLIFY (stride)
     135          92 :       ALLOCATE (stride(3))
     136         368 :       stride = section_get_ivals(dft_section, "PRINT%ENERGY_WINDOWS%STRIDE")
     137          92 :       CALL section_vals_val_get(dft_section, "PRINT%ENERGY_WINDOWS%EPS_FILTER", r_val=filter_eps)
     138          92 :       CALL section_vals_val_get(ls_scf_section, "EPS_LANCZOS", r_val=lanzcos_threshold)
     139          92 :       CALL section_vals_val_get(ls_scf_section, "MAX_ITER_LANCZOS", i_val=lanzcos_max_iter)
     140          92 :       CALL section_vals_val_get(ls_scf_section, "SIGN_SQRT_ORDER", i_val=newton_schulz_order)
     141             : 
     142             :       !Initialize data
     143          92 :       ALLOCATE (window, rho_ao_ortho)
     144          92 :       CALL dbcsr_get_info(matrix=matrix_ks(1)%matrix, nfullrows_total=nao)
     145         276 :       ALLOCATE (eigenvalues(nao))
     146          92 :       CALL dbcsr_create(tmp, template=matrix_s(1)%matrix, matrix_type="N")
     147          92 :       CALL dbcsr_create(S_minus_half, template=matrix_s(1)%matrix, matrix_type="N")
     148          92 :       CALL dbcsr_create(S_half, template=matrix_s(1)%matrix, matrix_type="N")
     149          92 :       CALL dbcsr_create(window, template=matrix_s(1)%matrix, matrix_type="N")
     150          92 :       CALL dbcsr_create(rho_ao_ortho, template=matrix_s(1)%matrix, matrix_type="N")
     151          92 :       CALL cp_fm_struct_create(fmstruct=ao_ao_fmstruct, context=blacs_env, nrow_global=nao, ncol_global=nao)
     152          92 :       CALL cp_fm_create(P_window_fm, ao_ao_fmstruct)
     153          92 :       CALL cp_fm_create(matrix_ks_fm, ao_ao_fmstruct)
     154          92 :       CALL cp_fm_create(rho_ao_ortho_fm, ao_ao_fmstruct)
     155          92 :       CALL cp_fm_create(S_minus_half_fm, ao_ao_fmstruct)
     156          92 :       CALL cp_fm_create(eigenvectors, ao_ao_fmstruct)
     157          92 :       CALL cp_fm_create(eigenvectors_nonorth, ao_ao_fmstruct)
     158          92 :       CALL auxbas_pw_pool%create_pw(rho_r)
     159          92 :       CALL auxbas_pw_pool%create_pw(rho_g)
     160             : 
     161             :       !calculate S_minus_half
     162             :       CALL matrix_sqrt_Newton_Schulz(S_half, S_minus_half, matrix_s(1)%matrix, filter_eps, &
     163          92 :                                      newton_schulz_order, lanzcos_threshold, lanzcos_max_iter)
     164             : 
     165             :       !get the full ks matrix
     166          92 :       CALL dbcsr_desymmetrize(matrix_ks(1)%matrix, matrix_ks_nosym)
     167             : 
     168             :       !switching to orthonormal basis
     169          92 :       CALL dbcsr_multiply("N", "N", one, S_minus_half, matrix_ks_nosym, zero, tmp, filter_eps=filter_eps)
     170          92 :       CALL dbcsr_multiply("N", "N", one, tmp, S_minus_half, zero, matrix_ks_nosym, filter_eps=filter_eps)
     171          92 :       CALL copy_dbcsr_to_fm(matrix_ks_nosym, matrix_ks_fm)
     172          92 :       CALL dbcsr_multiply("N", "N", one, S_half, rho_ao(1)%matrix, zero, tmp, filter_eps=filter_eps)
     173          92 :       CALL dbcsr_multiply("N", "N", one, tmp, S_half, zero, rho_ao_ortho, filter_eps=filter_eps)
     174          92 :       CALL copy_dbcsr_to_fm(rho_ao_ortho, rho_ao_ortho_fm)
     175             : 
     176             :       !diagonalize the full ks matrix
     177          92 :       CALL choose_eigv_solver(matrix_ks_fm, eigenvectors, eigenvalues)
     178          92 :       fermi_level = eigenvalues((nelectron_total + MOD(nelectron_total, 2))/2)
     179          92 :       IF (restrict_range) THEN
     180          12 :          lower_bound = MAX(fermi_level - energy_range, eigenvalues(1))
     181          12 :          upper_bound = MIN(fermi_level + energy_range, eigenvalues(SIZE(eigenvalues)))
     182             :       ELSE
     183          80 :          lower_bound = eigenvalues(1)
     184          80 :          upper_bound = eigenvalues(SIZE(eigenvalues))
     185             :       END IF
     186          92 :       IF (unit_nr > 0) THEN
     187          46 :          WRITE (unit_nr, *) " Creating energy windows. Fermi level: ", fermi_level
     188          46 :          WRITE (unit_nr, *) " Printing Energy Levels from ", lower_bound, " to ", upper_bound
     189             :       END IF
     190             :       !Rotate the eigenvectors back out of the orthonormal basis
     191          92 :       CALL copy_dbcsr_to_fm(S_minus_half, S_minus_half_fm)
     192             :       !calculate the density caused by the mos in the energy window
     193          92 :       CALL parallel_gemm("N", "N", nao, nao, nao, one, S_minus_half_fm, eigenvectors, zero, eigenvectors_nonorth)
     194             : 
     195             :       IF (debug) THEN
     196             :          !check difference to actual density
     197             :          CALL cp_fm_struct_create(fmstruct=window_fm_struct, context=blacs_env, nrow_global=nao, &
     198             :                                   ncol_global=nelectron_total/2)
     199             :          CALL cp_fm_create(window_fm, window_fm_struct)
     200             :          CALL cp_fm_to_fm(eigenvectors_nonorth, window_fm, nelectron_total/2, 1, 1)
     201             :          CALL parallel_gemm("N", "T", nao, nao, nelectron_total/2, 2*one, window_fm, window_fm, zero, P_window_fm)
     202             :          !ensure the correct sparsity
     203             :          CALL copy_fm_to_dbcsr(P_window_fm, tmp)
     204             :          CALL dbcsr_copy(window, matrix_ks(1)%matrix)
     205             :          CALL dbcsr_copy(window, tmp, keep_sparsity=.TRUE.)
     206             :          CALL calculate_rho_elec(matrix_p=window, &
     207             :                                  rho=rho_r, &
     208             :                                  rho_gspace=rho_g, &
     209             :                                  ks_env=ks_env)
     210             :          density_total = pw_integrate_function(rho_r)
     211             :          IF (unit_nr > 0) WRITE (unit_nr, *) " Ground-state density: ", density_total
     212             :          frob_norm = dbcsr_frobenius_norm(window)
     213             :          IF (unit_nr > 0) WRITE (unit_nr, *) " Frob norm of calculated ground-state density matrix: ", frob_norm
     214             :          CALL dbcsr_add(window, rho_ao(1)%matrix, one, -one)
     215             :          frob_norm = dbcsr_frobenius_norm(rho_ao(1)%matrix)
     216             :          IF (unit_nr > 0) WRITE (unit_nr, *) " Frob norm of current density matrix: ", frob_norm
     217             :          frob_norm = dbcsr_frobenius_norm(window)
     218             :          IF (unit_nr > 0) WRITE (unit_nr, *) " Difference between calculated ground-state density and current density: ", frob_norm
     219             :          CALL cp_fm_struct_release(window_fm_struct)
     220             :          CALL cp_fm_create(tmp_fm, ao_ao_fmstruct)
     221             :          CALL cp_fm_to_fm(rho_ao_ortho_fm, tmp_fm)
     222             :          CALL cp_fm_create(P_eigenvectors, ao_ao_fmstruct)
     223             :          ALLOCATE (P_eigenvalues(nao))
     224             :          CALL choose_eigv_solver(tmp_fm, P_eigenvectors, P_eigenvalues)
     225             :          CALL cp_fm_create(window_eigenvectors, ao_ao_fmstruct)
     226             :          ALLOCATE (window_eigenvalues(nao))
     227             :          CALL cp_fm_to_fm(eigenvectors, window_fm, nelectron_total/2, 1, 1)
     228             :          CALL parallel_gemm("N", "T", nao, nao, nelectron_total/2, 2*one, window_fm, window_fm, zero, P_window_fm)
     229             :          CALL choose_eigv_solver(P_window_fm, window_eigenvectors, window_eigenvalues)
     230             :          DO i = 1, nao
     231             :             IF (unit_nr > 0) THEN
     232             :               WRITE (unit_nr, *) i, "H:", eigenvalues(i), "P:", P_eigenvalues(nao - i + 1), "Pnew:", window_eigenvalues(nao - i + 1)
     233             :             END IF
     234             :          END DO
     235             :          DEALLOCATE (P_eigenvalues)
     236             :          CALL cp_fm_release(tmp_fm)
     237             :          CALL cp_fm_release(P_eigenvectors)
     238             :          DEALLOCATE (window_eigenvalues)
     239             :          CALL cp_fm_release(window_eigenvectors)
     240             :          CALL cp_fm_release(window_fm)
     241             :       END IF
     242             : 
     243             :       !create energy windows
     244          92 :       bin_width = (upper_bound - lower_bound)/nwindows
     245          92 :       next = 0
     246             : 
     247        7292 :       DO i = 1, nwindows
     248        7210 :          DO WHILE (eigenvalues(next + 1) < lower_bound)
     249        7200 :             next = next + 1
     250             :          END DO
     251             :          last = next
     252        9348 :          DO WHILE (eigenvalues(next + 1) < lower_bound + i*bin_width)
     253        2160 :             next = next + 1
     254        9348 :             IF (next == SIZE(eigenvalues)) EXIT
     255             :          END DO
     256             :          !calculate the occupation
     257             :          !not sure how bad this is now load balanced due to using the same blacs_env
     258        7200 :          CALL cp_fm_struct_create(fmstruct=window_fm_struct, context=blacs_env, nrow_global=nao, ncol_global=next - last)
     259        7200 :          CALL cp_fm_create(window_fm, window_fm_struct)
     260             :          !copy the mos in the energy window into a separate matrix
     261        7200 :          CALL cp_fm_to_fm(eigenvectors, window_fm, next - last, last + 1, 1)
     262        7200 :          CALL parallel_gemm("N", "T", nao, nao, next - last, one, window_fm, window_fm, zero, P_window_fm)
     263        7200 :          CALL cp_fm_trace(P_window_fm, rho_ao_ortho_fm, occupation)
     264        7200 :          IF (print_cube) THEN
     265         180 :             CALL cp_fm_to_fm(eigenvectors_nonorth, window_fm, next - last, last + 1, 1)
     266             :             !print the energy window to a cube file
     267             :             !calculate the density caused by the mos in the energy window
     268         180 :             CALL parallel_gemm("N", "T", nao, nao, next - last, one, window_fm, window_fm, zero, P_window_fm)
     269         180 :             CALL copy_fm_to_dbcsr(P_window_fm, tmp)
     270             :             !ensure the correct sparsity
     271         180 :             CALL dbcsr_copy(window, matrix_ks(1)%matrix)
     272         180 :             CALL dbcsr_copy(window, tmp, keep_sparsity=.TRUE.)
     273             :             CALL calculate_rho_elec(matrix_p=window, &
     274             :                                     rho=rho_r, &
     275             :                                     rho_gspace=rho_g, &
     276         180 :                                     ks_env=ks_env)
     277         180 :             WRITE (ext, "(A14,I5.5,A)") "-ENERGY-WINDOW", i, TRIM(cp_iter_string(logger%iter_info))//".cube"
     278         180 :             mpi_io = .TRUE.
     279             :             print_unit = cp_print_key_unit_nr(logger, dft_section, "PRINT%ENERGY_WINDOWS", &
     280             :                                               extension=ext, file_status="REPLACE", file_action="WRITE", &
     281         180 :                                               log_filename=.FALSE., mpi_io=mpi_io)
     282         180 :             WRITE (title, "(A14,I5)") "ENERGY WINDOW ", i
     283         180 :             CALL cp_pw_to_cube(rho_r, print_unit, title, particles=particles, stride=stride, mpi_io=mpi_io)
     284             :             CALL cp_print_key_finished_output(print_unit, logger, dft_section, &
     285         180 :                                               "PRINT%ENERGY_WINDOWS", mpi_io=mpi_io)
     286         180 :             density_ewindow_total = pw_integrate_function(rho_r)
     287         270 :             IF (unit_nr > 0) WRITE (unit_nr, "(A,F16.10,A,I5,A,F20.14,A,F20.14)") " Energy Level: ", &
     288          90 :                lower_bound + (i - 0.5_dp)*bin_width, " Number of states: ", next - last, " Occupation: ", &
     289         180 :                occupation, " Grid Density ", density_ewindow_total
     290             :          ELSE
     291        7020 :             IF (unit_nr > 0) THEN
     292        3510 :                WRITE (unit_nr, "(A,F16.10,A,I5,A,F20.14)") " Energy Level: ", lower_bound + (i - 0.5_dp)*bin_width, &
     293        7020 :                   " Number of states: ", next - last, " Occupation: ", occupation
     294             :             END IF
     295             :          END IF
     296        7200 :          CALL cp_fm_release(window_fm)
     297       21692 :          CALL cp_fm_struct_release(window_fm_struct)
     298             :       END DO
     299             : 
     300             :       !clean up
     301          92 :       CALL dbcsr_release(matrix_ks_nosym)
     302          92 :       CALL dbcsr_release(tmp)
     303          92 :       CALL dbcsr_release(window)
     304          92 :       CALL dbcsr_release(S_minus_half)
     305          92 :       CALL dbcsr_release(S_half)
     306          92 :       CALL dbcsr_release(rho_ao_ortho)
     307          92 :       DEALLOCATE (window, rho_ao_ortho)
     308          92 :       CALL cp_fm_struct_release(ao_ao_fmstruct)
     309          92 :       CALL cp_fm_release(matrix_ks_fm)
     310          92 :       CALL cp_fm_release(rho_ao_ortho_fm)
     311          92 :       CALL cp_fm_release(eigenvectors)
     312          92 :       CALL cp_fm_release(P_window_fm)
     313          92 :       CALL cp_fm_release(eigenvectors_nonorth)
     314          92 :       CALL cp_fm_release(S_minus_half_fm)
     315          92 :       CALL auxbas_pw_pool%give_back_pw(rho_r)
     316          92 :       CALL auxbas_pw_pool%give_back_pw(rho_g)
     317          92 :       DEALLOCATE (eigenvalues)
     318          92 :       DEALLOCATE (STRIDE)
     319             : 
     320          92 :       CALL timestop(handle)
     321             : 
     322         460 :    END SUBROUTINE energy_windows
     323             : 
     324             : !**************************************************************************************************
     325             : 
     326             : END MODULE qs_energy_window

Generated by: LCOV version 1.15