LCOV - code coverage report
Current view: top level - src - qs_dos.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 66.4 % 372 247
Test Date: 2026-07-25 06:35:44 Functions: 100.0 % 2 2

            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 Calculation and writing of density of  states
      10              : !> \par History
      11              : !>      -
      12              : !> \author JGH
      13              : ! **************************************************************************************************
      14              : MODULE qs_dos
      15              :    USE cp_array_utils,                  ONLY: cp_1d_r_p_type
      16              :    USE cp_control_types,                ONLY: dft_control_type
      17              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      18              :                                               cp_logger_get_default_io_unit,&
      19              :                                               cp_logger_type
      20              :    USE cp_output_handling,              ONLY: cp_p_file,&
      21              :                                               cp_print_key_finished_output,&
      22              :                                               cp_print_key_should_output,&
      23              :                                               cp_print_key_unit_nr
      24              :    USE input_section_types,             ONLY: section_vals_type,&
      25              :                                               section_vals_val_get
      26              :    USE kinds,                           ONLY: default_string_length,&
      27              :                                               dp
      28              :    USE kpoint_types,                    ONLY: kpoint_release,&
      29              :                                               kpoint_type
      30              :    USE message_passing,                 ONLY: mp_para_env_type
      31              :    USE qs_band_structure,               ONLY: calculate_kp_orbitals
      32              :    USE qs_dos_utils,                    ONLY: &
      33              :         add_broadened_peak, broadening_cutoff, dos_density_scale, dos_energy_label, &
      34              :         dos_energy_scale, dos_energy_unit_ev, dos_energy_zero_absolute, dos_energy_zero_auto, &
      35              :         dos_energy_zero_hoco, dos_energy_zero_label, dos_resolve_energy_zero, write_broadening_info
      36              :    USE qs_environment_types,            ONLY: get_qs_env,&
      37              :                                               qs_environment_type
      38              :    USE qs_mo_types,                     ONLY: get_mo_set,&
      39              :                                               mo_set_type
      40              : #include "./base/base_uses.f90"
      41              : 
      42              :    IMPLICIT NONE
      43              : 
      44              :    PRIVATE
      45              : 
      46              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_dos'
      47              : 
      48              :    PUBLIC :: calculate_dos, calculate_dos_kp
      49              : 
      50              : ! **************************************************************************************************
      51              : 
      52              : CONTAINS
      53              : 
      54              : ! **************************************************************************************************
      55              : !> \brief Compute and write density of states
      56              : !> \param mos ...
      57              : !> \param dft_section ...
      58              : !> \param unoccupied_evals ...
      59              : !> \param smearing_enabled ...
      60              : !> \param write_curve_output ...
      61              : !> \date    26.02.2008
      62              : !> \par History:
      63              : !> \author  JGH
      64              : !> \version 1.0
      65              : ! **************************************************************************************************
      66           66 :    SUBROUTINE calculate_dos(mos, dft_section, unoccupied_evals, smearing_enabled, write_curve_output)
      67              : 
      68              :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos
      69              :       TYPE(section_vals_type), POINTER                   :: dft_section
      70              :       TYPE(cp_1d_r_p_type), DIMENSION(:), OPTIONAL, &
      71              :          POINTER                                         :: unoccupied_evals
      72              :       LOGICAL, INTENT(IN), OPTIONAL                      :: smearing_enabled, write_curve_output
      73              : 
      74              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'calculate_dos'
      75              : 
      76              :       CHARACTER(LEN=16)                                  :: energy_label
      77              :       CHARACTER(LEN=20)                                  :: fmtstr_data
      78              :       CHARACTER(LEN=32)                                  :: zero_label
      79              :       CHARACTER(LEN=default_string_length)               :: my_act, my_pos
      80              :       INTEGER :: broaden_type, energy_unit, energy_zero, handle, i, iounit, ispin, iterstep, iv, &
      81              :          iw, ndigits, nhist, nmo(2), nspins, nstates(2), nvirt(2), resolved_energy_zero
      82              :       LOGICAL                                            :: append, do_broaden, &
      83              :                                                             fractional_occupation, ionode, &
      84              :                                                             should_output, smear_on
      85              :       REAL(KIND=dp) :: broaden_cutoff, broaden_width, de, density_factor, e1, e2, e_fermi(2), &
      86              :          emax, emin, energy_factor, energy_ref(2), ev_factor, eval, hoco(2), out_density, out_occ, &
      87              :          voigt_mixing
      88           66 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: ehist, hist, occval
      89           66 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: eigenvalues, occupation_numbers
      90              :       TYPE(cp_logger_type), POINTER                      :: logger
      91              :       TYPE(mo_set_type), POINTER                         :: mo_set
      92              : 
      93           66 :       NULLIFY (logger)
      94          132 :       logger => cp_get_default_logger()
      95              :       ionode = logger%para_env%is_source()
      96              :       should_output = BTEST(cp_print_key_should_output(logger%iter_info, dft_section, &
      97           66 :                                                        "PRINT%DOS"), cp_p_file)
      98           66 :       iounit = cp_logger_get_default_io_unit(logger)
      99           66 :       IF ((.NOT. should_output)) RETURN
     100              : 
     101           66 :       CALL timeset(routineN, handle)
     102           66 :       iterstep = logger%iter_info%iteration(logger%iter_info%n_rlevel)
     103              : 
     104           66 :       IF (iounit > 0) WRITE (UNIT=iounit, FMT='(/,(T3,A,T61,I10))') &
     105           33 :          " Calculate DOS at iteration step ", iterstep
     106              : 
     107           66 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%DELTA_E", r_val=de)
     108           66 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%APPEND", l_val=append)
     109           66 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%NDIGITS", i_val=ndigits)
     110           66 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%ENERGY_UNIT", i_val=energy_unit)
     111           66 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%ENERGY_ZERO", i_val=energy_zero)
     112           66 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%BROADEN%TYPE", i_val=broaden_type)
     113           66 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%BROADEN%WIDTH", r_val=broaden_width)
     114           66 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%BROADEN%VOIGT_MIXING", r_val=voigt_mixing)
     115           66 :       IF (append .AND. iterstep > 1) THEN
     116           10 :          my_pos = "APPEND"
     117              :       ELSE
     118           56 :          my_pos = "REWIND"
     119              :       END IF
     120           66 :       ndigits = MIN(MAX(ndigits, 1), 10)
     121           66 :       IF (PRESENT(write_curve_output)) THEN
     122            0 :          IF (write_curve_output .AND. de <= 0.0_dp) THEN
     123            0 :             CPWARN("Broadened DOS output requires DELTA_E > 0 and will be skipped")
     124            0 :             CALL timestop(handle)
     125            0 :             RETURN
     126              :          END IF
     127            0 :          IF (write_curve_output .AND. broaden_width <= 0.0_dp) THEN
     128            0 :             CPWARN("Broadened DOS output requires a finite WIDTH and will be skipped")
     129            0 :             CALL timestop(handle)
     130            0 :             RETURN
     131              :          END IF
     132              :       END IF
     133              :       do_broaden = .FALSE.
     134              :       IF (PRESENT(write_curve_output)) do_broaden = write_curve_output
     135           66 :       do_broaden = do_broaden .AND. (broaden_width > 0.0_dp)
     136            0 :       IF (do_broaden) de = MAX(de, 0.00001_dp)
     137              : 
     138           66 :       emin = 1.e10_dp
     139           66 :       emax = -1.e10_dp
     140           66 :       nspins = SIZE(mos)
     141           66 :       nmo(:) = 0
     142           66 :       nvirt(:) = 0
     143           66 :       nstates(:) = 0
     144          198 :       hoco(:) = -HUGE(0.0_dp)
     145           66 :       fractional_occupation = .FALSE.
     146           66 :       smear_on = .FALSE.
     147           66 :       IF (PRESENT(smearing_enabled)) smear_on = smearing_enabled
     148              : 
     149          132 :       DO ispin = 1, nspins
     150           66 :          mo_set => mos(ispin)
     151           66 :          CALL get_mo_set(mo_set=mo_set, nmo=nmo(ispin), mu=e_fermi(ispin))
     152           66 :          eigenvalues => mo_set%eigenvalues
     153           66 :          occupation_numbers => mo_set%occupation_numbers
     154          642 :          DO i = 1, nmo(ispin)
     155          576 :             IF (occupation_numbers(i) > 1.0e-10_dp) hoco(ispin) = MAX(hoco(ispin), eigenvalues(i))
     156          576 :             IF (ABS(occupation_numbers(i) - REAL(NINT(occupation_numbers(i)), KIND=dp)) > &
     157           74 :                 1.0e-8_dp) fractional_occupation = .TRUE.
     158              :          END DO
     159           66 :          IF (hoco(ispin) < -0.5_dp*HUGE(0.0_dp)) hoco(ispin) = e_fermi(ispin)
     160           66 :          IF (PRESENT(unoccupied_evals)) THEN
     161            2 :             IF (ASSOCIATED(unoccupied_evals(ispin)%array)) nvirt(ispin) = SIZE(unoccupied_evals(ispin)%array)
     162              :          END IF
     163           66 :          nstates(ispin) = nmo(ispin) + nvirt(ispin)
     164          642 :          e1 = MINVAL(eigenvalues(1:nmo(ispin)))
     165          642 :          e2 = MAXVAL(eigenvalues(1:nmo(ispin)))
     166           66 :          IF (nvirt(ispin) > 0) THEN
     167           12 :             e1 = MIN(e1, MINVAL(unoccupied_evals(ispin)%array(1:nvirt(ispin))))
     168           12 :             e2 = MAX(e2, MAXVAL(unoccupied_evals(ispin)%array(1:nvirt(ispin))))
     169              :          END IF
     170           66 :          emin = MIN(emin, e1)
     171          132 :          emax = MAX(emax, e2)
     172              :       END DO
     173              : 
     174           66 :       IF (do_broaden) THEN
     175            0 :          broaden_cutoff = broadening_cutoff(broaden_type, broaden_width)
     176            0 :          emin = emin - broaden_cutoff
     177            0 :          emax = emax + broaden_cutoff
     178            0 :          nhist = NINT((emax - emin)/de) + 1
     179            0 :          ALLOCATE (hist(nhist, nspins), occval(nhist, nspins), ehist(nhist, nspins))
     180            0 :          hist = 0.0_dp
     181            0 :          occval = 0.0_dp
     182            0 :          ehist = 0.0_dp
     183            0 :          DO ispin = 1, nspins
     184            0 :             mo_set => mos(ispin)
     185            0 :             occupation_numbers => mo_set%occupation_numbers
     186            0 :             eigenvalues => mo_set%eigenvalues
     187            0 :             DO i = 1, nmo(ispin)
     188              :                CALL add_broadened_peak(hist(:, ispin), occval(:, ispin), emin, de, eigenvalues(i), &
     189              :                                        occupation_numbers(i), 1.0_dp, broaden_type, broaden_width, &
     190            0 :                                        voigt_mixing)
     191              :             END DO
     192            0 :             DO i = 1, nvirt(ispin)
     193              :                CALL add_broadened_peak(hist(:, ispin), occval(:, ispin), emin, de, &
     194              :                                        unoccupied_evals(ispin)%array(i), 0.0_dp, 1.0_dp, &
     195            0 :                                        broaden_type, broaden_width, voigt_mixing)
     196              :             END DO
     197              :          END DO
     198            0 :          DO i = 1, nhist
     199            0 :             ehist(i, 1:nspins) = emin + (i - 1)*de
     200              :          END DO
     201           66 :       ELSE IF (de > 0.0_dp) THEN
     202           66 :          nhist = NINT((emax - emin)/de) + 1
     203          528 :          ALLOCATE (hist(nhist, nspins), occval(nhist, nspins), ehist(nhist, nspins))
     204           66 :          hist = 0.0_dp
     205           66 :          occval = 0.0_dp
     206           66 :          ehist = 0.0_dp
     207          132 :          DO ispin = 1, nspins
     208           66 :             mo_set => mos(ispin)
     209           66 :             occupation_numbers => mo_set%occupation_numbers
     210           66 :             eigenvalues => mo_set%eigenvalues
     211          642 :             DO i = 1, nmo(ispin)
     212          576 :                eval = eigenvalues(i) - emin
     213          576 :                iv = NINT(eval/de) + 1
     214          576 :                CPASSERT((iv > 0) .AND. (iv <= nhist))
     215          576 :                hist(iv, ispin) = hist(iv, ispin) + 1.0_dp
     216          642 :                occval(iv, ispin) = occval(iv, ispin) + occupation_numbers(i)
     217              :             END DO
     218           76 :             DO i = 1, nvirt(ispin)
     219           10 :                eval = unoccupied_evals(ispin)%array(i) - emin
     220           10 :                iv = NINT(eval/de) + 1
     221           10 :                CPASSERT((iv > 0) .AND. (iv <= nhist))
     222           76 :                hist(iv, ispin) = hist(iv, ispin) + 1.0_dp
     223              :             END DO
     224        73352 :             hist(:, ispin) = hist(:, ispin)/REAL(nstates(ispin), KIND=dp)
     225              :          END DO
     226        73286 :          DO i = 1, nhist
     227       146506 :             ehist(i, 1:nspins) = emin + (i - 1)*de
     228              :          END DO
     229              :       ELSE
     230            0 :          nhist = MAXVAL(nstates)
     231            0 :          ALLOCATE (hist(nhist, nspins), occval(nhist, nspins), ehist(nhist, nspins))
     232            0 :          hist = 0.0_dp
     233            0 :          occval = 0.0_dp
     234            0 :          ehist = 0.0_dp
     235            0 :          DO ispin = 1, nspins
     236            0 :             mo_set => mos(ispin)
     237            0 :             occupation_numbers => mo_set%occupation_numbers
     238            0 :             eigenvalues => mo_set%eigenvalues
     239            0 :             DO i = 1, nmo(ispin)
     240            0 :                ehist(i, ispin) = eigenvalues(i)
     241            0 :                hist(i, ispin) = 1.0_dp
     242            0 :                occval(i, ispin) = occupation_numbers(i)
     243              :             END DO
     244            0 :             DO i = 1, nvirt(ispin)
     245            0 :                ehist(nmo(ispin) + i, ispin) = unoccupied_evals(ispin)%array(i)
     246            0 :                hist(nmo(ispin) + i, ispin) = 1.0_dp
     247              :             END DO
     248            0 :             hist(:, ispin) = hist(:, ispin)/REAL(nstates(ispin), KIND=dp)
     249              :          END DO
     250              :       END IF
     251              : 
     252           66 :       resolved_energy_zero = dos_resolve_energy_zero(energy_zero, smear_on, fractional_occupation)
     253            0 :       SELECT CASE (resolved_energy_zero)
     254              :       CASE (dos_energy_zero_absolute)
     255            0 :          energy_ref(:) = 0.0_dp
     256              :       CASE (dos_energy_zero_hoco)
     257          248 :          energy_ref(:) = MAXVAL(hoco(1:nspins))
     258              :       CASE DEFAULT
     259           78 :          energy_ref(:) = MAXVAL(e_fermi(1:nspins))
     260              :       END SELECT
     261           66 :       IF (.NOT. do_broaden) energy_ref(:) = 0.0_dp
     262           66 :       energy_factor = MERGE(dos_energy_scale(energy_unit), 1.0_dp, do_broaden)
     263           66 :       density_factor = MERGE(dos_density_scale(energy_unit), 1.0_dp, do_broaden)
     264           66 :       energy_label = dos_energy_label(MERGE(energy_unit, 1, do_broaden))
     265           66 :       zero_label = dos_energy_zero_label(resolved_energy_zero)
     266           66 :       IF (energy_zero == dos_energy_zero_auto) zero_label = "AUTO -> "//TRIM(zero_label)
     267           66 :       ev_factor = dos_energy_scale(dos_energy_unit_ev)
     268              : 
     269           66 :       my_act = "WRITE"
     270           66 :       IF (do_broaden) THEN
     271              :          iw = cp_print_key_unit_nr(logger, dft_section, "PRINT%DOS", &
     272              :                                    extension=".dos", file_position=my_pos, file_action=my_act, &
     273            0 :                                    file_form="FORMATTED", middle_name="curve")
     274              :       ELSE
     275              :          iw = cp_print_key_unit_nr(logger, dft_section, "PRINT%DOS", &
     276              :                                    extension=".dos", file_position=my_pos, file_action=my_act, &
     277           66 :                                    file_form="FORMATTED")
     278              :       END IF
     279           66 :       IF (iw > 0) THEN
     280           33 :          WRITE (UNIT=iw, FMT="(A,I0)") "# DOS at iteration step i = ", iterstep
     281           33 :          IF (nspins == 2) THEN
     282              :             WRITE (UNIT=iw, FMT="(A,2F12.6,A,2F12.6,A)") &
     283            0 :                "# E(Fermi) = ", e_fermi(1:2), " a.u. = ", e_fermi(1:2)*ev_factor, " eV"
     284              :             WRITE (UNIT=iw, FMT="(A,2F12.6,A,2F12.6,A)") &
     285            0 :                "# E(HOCO)  = ", hoco(1:2), " a.u. = ", hoco(1:2)*ev_factor, " eV"
     286            0 :             IF (do_broaden) THEN
     287            0 :                WRITE (UNIT=iw, FMT="(A,A)") "# Energy zero: ", TRIM(zero_label)
     288            0 :                CALL write_broadening_info(iw, broaden_type, broaden_width, voigt_mixing)
     289              :             END IF
     290            0 :             IF (do_broaden .OR. de > 0.0_dp) THEN
     291            0 :                WRITE (UNIT=iw, FMT="(A,A,A)") "#  "//TRIM(energy_label)//"  Alpha_Density    Occupation", &
     292            0 :                   "    Beta_Density     Occupation"
     293            0 :                WRITE (UNIT=fmtstr_data, FMT="(A,I0,A)") "(F15.8,4F20.", ndigits, ")"
     294              :             ELSE
     295            0 :                WRITE (UNIT=iw, FMT="(A,A,A)") "#  "//TRIM(energy_label)//"  Alpha_Density    Occupation", &
     296            0 :                   "    "//TRIM(energy_label), "  Beta_Density     Occupation"
     297            0 :                WRITE (UNIT=fmtstr_data, FMT="(A,I0,A)") "(2(F15.8,2F20.", ndigits, "))"
     298              :             END IF
     299              :          ELSE
     300              :             WRITE (UNIT=iw, FMT="(A,F12.6,A,F12.6,A)") &
     301           33 :                "# E(Fermi) = ", e_fermi(1), " a.u. = ", e_fermi(1)*ev_factor, " eV"
     302              :             WRITE (UNIT=iw, FMT="(A,F12.6,A,F12.6,A)") &
     303           33 :                "# E(HOCO)  = ", hoco(1), " a.u. = ", hoco(1)*ev_factor, " eV"
     304           33 :             IF (do_broaden) THEN
     305            0 :                WRITE (UNIT=iw, FMT="(A,A)") "# Energy zero: ", TRIM(zero_label)
     306            0 :                CALL write_broadening_info(iw, broaden_type, broaden_width, voigt_mixing)
     307              :             END IF
     308           33 :             WRITE (UNIT=iw, FMT="(A,A)") "# "//TRIM(energy_label), "       Density     Occupation"
     309              :             ! (F15.8,2F20.ndigits)
     310           33 :             WRITE (UNIT=fmtstr_data, FMT="(A,I0,A)") "(F15.8,2F20.", ndigits, ")"
     311              :          END IF
     312        36643 :          DO i = 1, nhist
     313        36643 :             IF (nspins == 2) THEN
     314            0 :                IF (do_broaden) THEN
     315            0 :                   eval = (ehist(i, 1) - energy_ref(1))*energy_factor
     316            0 :                   WRITE (UNIT=iw, FMT=fmtstr_data) eval, hist(i, 1)*density_factor, &
     317            0 :                      occval(i, 1)*density_factor, hist(i, 2)*density_factor, &
     318            0 :                      occval(i, 2)*density_factor
     319            0 :                ELSE IF (de > 0.0_dp) THEN
     320              :                   IF (hist(i, 1) == 0.0_dp .AND. occval(i, 1) == 0.0_dp .AND. &
     321            0 :                       hist(i, 2) == 0.0_dp .AND. occval(i, 2) == 0.0_dp) CYCLE
     322            0 :                   eval = ehist(i, 1)
     323            0 :                   WRITE (UNIT=iw, FMT=fmtstr_data) eval, hist(i, 1), occval(i, 1), &
     324            0 :                      hist(i, 2), occval(i, 2)
     325              :                ELSE
     326            0 :                   e1 = ehist(i, 1)
     327            0 :                   e2 = ehist(i, 2)
     328            0 :                   WRITE (UNIT=iw, FMT=fmtstr_data) e1, hist(i, 1), occval(i, 1), &
     329            0 :                      e2, hist(i, 2), occval(i, 2)
     330              :                END IF
     331              :             ELSE
     332        36610 :                eval = (ehist(i, 1) - energy_ref(1))*energy_factor
     333              :                ! fmtstr_data == "(F15.8,2F20.xx)"
     334        36610 :                IF (do_broaden) THEN
     335            0 :                   out_density = hist(i, 1)*density_factor
     336            0 :                   out_occ = occval(i, 1)*density_factor
     337              :                ELSE
     338        36610 :                   out_density = hist(i, 1)
     339        36610 :                   out_occ = occval(i, 1)
     340        36610 :                   IF (out_density == 0.0_dp .AND. out_occ == 0.0_dp) CYCLE
     341              :                END IF
     342          271 :                WRITE (UNIT=iw, FMT=fmtstr_data) eval, out_density, out_occ
     343              :             END IF
     344              :          END DO
     345              :       END IF
     346           66 :       CALL cp_print_key_finished_output(iw, logger, dft_section, "PRINT%DOS")
     347           66 :       DEALLOCATE (hist, occval, ehist)
     348              : 
     349           66 :       CALL timestop(handle)
     350              : 
     351           66 :    END SUBROUTINE calculate_dos
     352              : 
     353              : ! **************************************************************************************************
     354              : !> \brief Compute and write density of states (kpoints)
     355              : !> \param qs_env ...
     356              : !> \param dft_section ...
     357              : !> \param write_curve_output ...
     358              : !> \date    26.02.2008
     359              : !> \par History:
     360              : !> \author  JGH
     361              : !> \version 1.0
     362              : ! **************************************************************************************************
     363           20 :    SUBROUTINE calculate_dos_kp(qs_env, dft_section, write_curve_output)
     364              : 
     365              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     366              :       TYPE(section_vals_type), POINTER                   :: dft_section
     367              :       LOGICAL, INTENT(IN), OPTIONAL                      :: write_curve_output
     368              : 
     369              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'calculate_dos_kp'
     370              : 
     371              :       CHARACTER(LEN=16)                                  :: energy_label, fmtstr_data
     372              :       CHARACTER(LEN=32)                                  :: zero_label
     373              :       CHARACTER(LEN=default_string_length)               :: err, my_act, my_pos
     374              :       INTEGER :: broaden_type, energy_unit, energy_zero, fractional_occupation_int, handle, i, ik, &
     375              :          iounit, ispin, iterstep, iv, iw, ndigits, nhist, nmo(2), nmo_kp, nspins, &
     376              :          resolved_energy_zero
     377           20 :       INTEGER, DIMENSION(:), POINTER                     :: nkp_grid
     378              :       LOGICAL                                            :: append, do_broaden, explicit, &
     379              :                                                             fractional_occupation, ionode, &
     380              :                                                             should_output
     381              :       REAL(KIND=dp) :: broaden_cutoff, broaden_width, de, density_factor, e1, e2, e_fermi(2), &
     382              :          emax, emin, energy_factor, energy_ref(2), ev_factor, eval, hoco(2), out_density, out_occ, &
     383              :          voigt_mixing, wkp
     384           20 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: ehist, hist, occval
     385           20 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: eigenvalues, occupation_numbers
     386              :       TYPE(cp_logger_type), POINTER                      :: logger
     387              :       TYPE(dft_control_type), POINTER                    :: dft_control
     388              :       TYPE(kpoint_type), POINTER                         :: kpoints
     389           20 :       TYPE(mo_set_type), DIMENSION(:, :), POINTER        :: mos
     390              :       TYPE(mo_set_type), POINTER                         :: mo_set
     391              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     392              : 
     393           20 :       NULLIFY (logger, kpoints)
     394           40 :       logger => cp_get_default_logger()
     395              :       ionode = logger%para_env%is_source()
     396              :       should_output = BTEST(cp_print_key_should_output(logger%iter_info, dft_section, &
     397           20 :                                                        "PRINT%DOS"), cp_p_file)
     398           20 :       iounit = cp_logger_get_default_io_unit(logger)
     399           20 :       IF ((.NOT. should_output)) RETURN
     400              : 
     401           20 :       CALL timeset(routineN, handle)
     402           20 :       iterstep = logger%iter_info%iteration(logger%iter_info%n_rlevel)
     403              : 
     404              :       ! check whether the user requested a different MP grid for the DOS
     405           20 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%MP_GRID", i_vals=nkp_grid, explicit=explicit)
     406              : 
     407           20 :       IF (explicit) THEN
     408              :          ! make sure is a valid grid
     409            0 :          DO i = 1, 3
     410            0 :             IF (nkp_grid(i) < 1) THEN
     411              :                WRITE (UNIT=err, FMT='(T4,A,I3,A,I1)') &
     412            0 :                   "Invalid kpoint grid for DOS ", nkp_grid(i), " in dimension ", i
     413            0 :                CPABORT(TRIM(err))
     414              :             END IF
     415              :          END DO
     416              :          ! calculate orbitals and energies
     417            0 :          CALL calculate_kp_orbitals(qs_env, kpoints, "MONKHORST-PACK", 0, nkp_grid)
     418              :       ELSE
     419              :          ! use the kpoints from the environment
     420           20 :          CALL get_qs_env(qs_env, kpoints=kpoints)
     421              :       END IF
     422              : 
     423           20 :       IF (iounit > 0) WRITE (UNIT=iounit, FMT='(/,(T3,A,T61,I10))') &
     424           10 :          " Calculate DOS at iteration step ", iterstep
     425              :       IF (iounit > 0) WRITE (UNIT=iounit, FMT='((T3,A,3I3,A))') &
     426           40 :          " Using a", kpoints%nkp_grid(:), ' '//TRIM(kpoints%kp_scheme)//' grid'
     427              : 
     428           20 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%DELTA_E", r_val=de)
     429           20 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%APPEND", l_val=append)
     430           20 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%NDIGITS", i_val=ndigits)
     431           20 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%ENERGY_UNIT", i_val=energy_unit)
     432           20 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%ENERGY_ZERO", i_val=energy_zero)
     433           20 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%BROADEN%TYPE", i_val=broaden_type)
     434           20 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%BROADEN%WIDTH", r_val=broaden_width)
     435           20 :       CALL section_vals_val_get(dft_section, "PRINT%DOS%CURVE%BROADEN%VOIGT_MIXING", r_val=voigt_mixing)
     436           20 :       IF (append .AND. iterstep > 1) THEN
     437            0 :          my_pos = "APPEND"
     438              :       ELSE
     439           20 :          my_pos = "REWIND"
     440              :       END IF
     441           20 :       ndigits = MIN(MAX(ndigits, 1), 10)
     442           20 :       IF (PRESENT(write_curve_output)) THEN
     443            0 :          IF (write_curve_output .AND. de <= 0.0_dp) THEN
     444            0 :             CPWARN("Broadened k-point DOS output requires DELTA_E > 0 and will be skipped")
     445            0 :             CALL timestop(handle)
     446            0 :             IF (explicit) CALL kpoint_release(kpoints)
     447            0 :             RETURN
     448              :          END IF
     449            0 :          IF (write_curve_output .AND. broaden_width <= 0.0_dp) THEN
     450            0 :             CPWARN("Broadened k-point DOS output requires a finite WIDTH and will be skipped")
     451            0 :             CALL timestop(handle)
     452            0 :             IF (explicit) CALL kpoint_release(kpoints)
     453            0 :             RETURN
     454              :          END IF
     455           20 :       ELSE IF (de <= 0.0_dp) THEN
     456            0 :          CPWARN("K-point DOS output requires DELTA_E > 0 and will be skipped")
     457            0 :          CALL timestop(handle)
     458            0 :          IF (explicit) CALL kpoint_release(kpoints)
     459            0 :          RETURN
     460              :       END IF
     461              :       ! ensure a lower value for the DOS grid width
     462           20 :       de = MAX(de, 0.00001_dp)
     463           20 :       do_broaden = .FALSE.
     464           20 :       IF (PRESENT(write_curve_output)) do_broaden = write_curve_output
     465            0 :       do_broaden = do_broaden .AND. (broaden_width > 0.0_dp)
     466              : 
     467           20 :       CALL get_qs_env(qs_env, dft_control=dft_control)
     468           20 :       nspins = dft_control%nspins
     469           20 :       para_env => kpoints%para_env_inter_kp
     470              : 
     471           20 :       emin = 1.e10_dp
     472           20 :       emax = -1.e10_dp
     473           20 :       nmo(:) = 0
     474           20 :       e_fermi(:) = 0.0_dp
     475           60 :       hoco(:) = -HUGE(0.0_dp)
     476           20 :       fractional_occupation = .FALSE.
     477           20 :       IF (kpoints%nkp /= 0) THEN
     478          132 :          DO ik = 1, SIZE(kpoints%kp_env)
     479          112 :             mos => kpoints%kp_env(ik)%kpoint_env%mos
     480          112 :             CPASSERT(ASSOCIATED(mos))
     481          244 :             DO ispin = 1, nspins
     482          112 :                mo_set => mos(1, ispin)
     483          112 :                CALL get_mo_set(mo_set=mo_set, nmo=nmo_kp, mu=e_fermi(ispin))
     484          112 :                eigenvalues => mo_set%eigenvalues
     485          112 :                occupation_numbers => mo_set%occupation_numbers
     486         2334 :                DO i = 1, nmo_kp
     487         2222 :                   IF (occupation_numbers(i) > 1.0e-10_dp) hoco(ispin) = MAX(hoco(ispin), eigenvalues(i))
     488         2222 :                   IF (ABS(occupation_numbers(i) - REAL(NINT(occupation_numbers(i)), KIND=dp)) > &
     489          314 :                       1.0e-8_dp) fractional_occupation = .TRUE.
     490              :                END DO
     491         2334 :                e1 = MINVAL(eigenvalues(1:nmo_kp))
     492         2334 :                e2 = MAXVAL(eigenvalues(1:nmo_kp))
     493          112 :                emin = MIN(emin, e1)
     494          112 :                emax = MAX(emax, e2)
     495          336 :                nmo(ispin) = MAX(nmo(ispin), nmo_kp)
     496              :             END DO
     497              :          END DO
     498              :       END IF
     499           20 :       CALL para_env%min(emin)
     500           20 :       CALL para_env%max(emax)
     501           20 :       CALL para_env%max(nmo)
     502           20 :       CALL para_env%max(e_fermi)
     503           20 :       CALL para_env%max(hoco)
     504           20 :       fractional_occupation_int = MERGE(1, 0, fractional_occupation)
     505           20 :       CALL para_env%max(fractional_occupation_int)
     506           20 :       fractional_occupation = (fractional_occupation_int /= 0)
     507           40 :       DO ispin = 1, nspins
     508           40 :          IF (hoco(ispin) < -0.5_dp*HUGE(0.0_dp)) hoco(ispin) = e_fermi(ispin)
     509              :       END DO
     510              : 
     511           20 :       IF (do_broaden) THEN
     512            0 :          broaden_cutoff = broadening_cutoff(broaden_type, broaden_width)
     513            0 :          emin = emin - broaden_cutoff
     514            0 :          emax = emax + broaden_cutoff
     515              :       END IF
     516           20 :       nhist = NINT((emax - emin)/de) + 1
     517          160 :       ALLOCATE (hist(nhist, nspins), occval(nhist, nspins), ehist(nhist, nspins))
     518           20 :       hist = 0.0_dp
     519           20 :       occval = 0.0_dp
     520           20 :       ehist = 0.0_dp
     521              : 
     522           20 :       IF (kpoints%nkp /= 0) THEN
     523          132 :          DO ik = 1, SIZE(kpoints%kp_env)
     524          112 :             mos => kpoints%kp_env(ik)%kpoint_env%mos
     525          112 :             wkp = kpoints%kp_env(ik)%kpoint_env%wkp
     526          244 :             DO ispin = 1, nspins
     527          112 :                mo_set => mos(1, ispin)
     528          112 :                occupation_numbers => mo_set%occupation_numbers
     529          112 :                eigenvalues => mo_set%eigenvalues
     530          224 :                IF (do_broaden) THEN
     531            0 :                   DO i = 1, nmo(ispin)
     532              :                      CALL add_broadened_peak(hist(:, ispin), occval(:, ispin), emin, de, eigenvalues(i), &
     533              :                                              occupation_numbers(i), wkp, broaden_type, broaden_width, &
     534            0 :                                              voigt_mixing)
     535              :                   END DO
     536              :                ELSE
     537         2334 :                   DO i = 1, nmo(ispin)
     538         2222 :                      eval = eigenvalues(i) - emin
     539         2222 :                      iv = NINT(eval/de) + 1
     540         2222 :                      CPASSERT((iv > 0) .AND. (iv <= nhist))
     541         2222 :                      hist(iv, ispin) = hist(iv, ispin) + wkp
     542         2334 :                      occval(iv, ispin) = occval(iv, ispin) + wkp*occupation_numbers(i)
     543              :                   END DO
     544              :                END IF
     545              :             END DO
     546              :          END DO
     547              :       END IF
     548           20 :       CALL para_env%sum(hist)
     549           20 :       CALL para_env%sum(occval)
     550           20 :       IF (.NOT. do_broaden) THEN
     551           40 :          DO ispin = 1, nspins
     552       137324 :             hist(:, ispin) = hist(:, ispin)/REAL(nmo(ispin), KIND=dp)
     553              :          END DO
     554              :       END IF
     555       137304 :       DO i = 1, nhist
     556       274588 :          ehist(i, 1:nspins) = emin + (i - 1)*de
     557              :       END DO
     558              : 
     559           20 :       resolved_energy_zero = dos_resolve_energy_zero(energy_zero, dft_control%smear, fractional_occupation)
     560            0 :       SELECT CASE (resolved_energy_zero)
     561              :       CASE (dos_energy_zero_absolute)
     562            0 :          energy_ref(:) = 0.0_dp
     563              :       CASE (dos_energy_zero_hoco)
     564           16 :          energy_ref(:) = MAXVAL(hoco(1:nspins))
     565              :       CASE DEFAULT
     566           68 :          energy_ref(:) = MAXVAL(e_fermi(1:nspins))
     567              :       END SELECT
     568           20 :       IF (.NOT. do_broaden) energy_ref(:) = 0.0_dp
     569           20 :       energy_factor = MERGE(dos_energy_scale(energy_unit), 1.0_dp, do_broaden)
     570           20 :       density_factor = MERGE(dos_density_scale(energy_unit), 1.0_dp, do_broaden)
     571           20 :       energy_label = dos_energy_label(MERGE(energy_unit, 1, do_broaden))
     572           20 :       zero_label = dos_energy_zero_label(resolved_energy_zero)
     573           20 :       IF (energy_zero == dos_energy_zero_auto) zero_label = "AUTO -> "//TRIM(zero_label)
     574           20 :       ev_factor = dos_energy_scale(dos_energy_unit_ev)
     575              : 
     576           20 :       my_act = "WRITE"
     577           20 :       IF (do_broaden) THEN
     578              :          iw = cp_print_key_unit_nr(logger, dft_section, "PRINT%DOS", &
     579              :                                    extension=".dos", file_position=my_pos, file_action=my_act, &
     580            0 :                                    file_form="FORMATTED", middle_name="curve")
     581              :       ELSE
     582              :          iw = cp_print_key_unit_nr(logger, dft_section, "PRINT%DOS", &
     583              :                                    extension=".dos", file_position=my_pos, file_action=my_act, &
     584           20 :                                    file_form="FORMATTED")
     585              :       END IF
     586           20 :       IF (iw > 0) THEN
     587           10 :          WRITE (UNIT=iw, FMT="(A,I0)") "# DOS at iteration step i = ", iterstep
     588           10 :          IF (nspins == 2) THEN
     589              :             WRITE (UNIT=iw, FMT="(A,2F12.6,A,2F12.6,A)") &
     590            0 :                "# E(Fermi) = ", e_fermi(1:2), " a.u. = ", e_fermi(1:2)*ev_factor, " eV"
     591              :             WRITE (UNIT=iw, FMT="(A,2F12.6,A,2F12.6,A)") &
     592            0 :                "# E(HOCO)  = ", hoco(1:2), " a.u. = ", hoco(1:2)*ev_factor, " eV"
     593            0 :             IF (do_broaden) THEN
     594            0 :                WRITE (UNIT=iw, FMT="(A,A)") "# Energy zero: ", TRIM(zero_label)
     595            0 :                CALL write_broadening_info(iw, broaden_type, broaden_width, voigt_mixing)
     596              :             END IF
     597            0 :             WRITE (UNIT=iw, FMT="(A,A)") "#  "//TRIM(energy_label)//"  Alpha_Density    Occupation", &
     598            0 :                "   Beta_Density     Occupation"
     599              :             ! (F15.8,4F20.ndigits)
     600            0 :             WRITE (UNIT=fmtstr_data, FMT="(A,I0,A)") "(F15.8,4F20.", ndigits, ")"
     601              :          ELSE
     602              :             WRITE (UNIT=iw, FMT="(A,F12.6,A,F12.6,A)") &
     603           10 :                "# E(Fermi) = ", e_fermi(1), " a.u. = ", e_fermi(1)*ev_factor, " eV"
     604              :             WRITE (UNIT=iw, FMT="(A,F12.6,A,F12.6,A)") &
     605           10 :                "# E(HOCO)  = ", hoco(1), " a.u. = ", hoco(1)*ev_factor, " eV"
     606           10 :             IF (do_broaden) THEN
     607            0 :                WRITE (UNIT=iw, FMT="(A,A)") "# Energy zero: ", TRIM(zero_label)
     608            0 :                CALL write_broadening_info(iw, broaden_type, broaden_width, voigt_mixing)
     609              :             END IF
     610           10 :             WRITE (UNIT=iw, FMT="(A,A)") "#  "//TRIM(energy_label), "       Density     Occupation"
     611              :             ! (F15.8,2F20.ndigits)
     612           10 :             WRITE (UNIT=fmtstr_data, FMT="(A,I0,A)") "(F15.8,2F20.", ndigits, ")"
     613              :          END IF
     614        68652 :          DO i = 1, nhist
     615        68642 :             eval = (ehist(i, 1) - energy_ref(1))*energy_factor
     616        68652 :             IF (nspins == 2) THEN
     617              :                ! fmtstr_data == "(F15.8,4F20.xx)"
     618            0 :                IF (do_broaden) THEN
     619            0 :                   WRITE (UNIT=iw, FMT=fmtstr_data) eval, hist(i, 1)*density_factor, &
     620            0 :                      occval(i, 1)*density_factor, hist(i, 2)*density_factor, occval(i, 2)*density_factor
     621              :                ELSE
     622              :                   IF (hist(i, 1) == 0.0_dp .AND. occval(i, 1) == 0.0_dp .AND. &
     623            0 :                       hist(i, 2) == 0.0_dp .AND. occval(i, 2) == 0.0_dp) CYCLE
     624            0 :                   WRITE (UNIT=iw, FMT=fmtstr_data) eval, hist(i, 1), occval(i, 1), &
     625            0 :                      hist(i, 2), occval(i, 2)
     626              :                END IF
     627              :             ELSE
     628              :                ! fmtstr_data == "(F15.8,2F20.xx)"
     629        68642 :                IF (do_broaden) THEN
     630            0 :                   out_density = hist(i, 1)*density_factor
     631            0 :                   out_occ = occval(i, 1)*density_factor
     632              :                ELSE
     633        68642 :                   out_density = hist(i, 1)
     634        68642 :                   out_occ = occval(i, 1)
     635        68642 :                   IF (out_density == 0.0_dp .AND. out_occ == 0.0_dp) CYCLE
     636              :                END IF
     637          813 :                WRITE (UNIT=iw, FMT=fmtstr_data) eval, out_density, out_occ
     638              :             END IF
     639              :          END DO
     640              :       END IF
     641           20 :       CALL cp_print_key_finished_output(iw, logger, dft_section, "PRINT%DOS")
     642           20 :       DEALLOCATE (hist, occval, ehist)
     643              : 
     644              :       ! destroy the extra k-point set if it was created
     645           20 :       IF (explicit) THEN
     646            0 :          CALL kpoint_release(kpoints)
     647              :       END IF
     648              : 
     649           20 :       CALL timestop(handle)
     650              : 
     651           80 :    END SUBROUTINE calculate_dos_kp
     652              : 
     653              : END MODULE qs_dos
        

Generated by: LCOV version 2.0-1