LCOV - code coverage report
Current view: top level - src - qs_dos_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:06f838d) Lines: 88.5 % 87 77
Test Date: 2026-06-05 07:04:50 Functions: 100.0 % 11 11

            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 Utilities for broadened DOS and PDOS output.
      10              : ! **************************************************************************************************
      11              : MODULE qs_dos_utils
      12              :    USE cp_units,                        ONLY: cp_unit_from_cp2k
      13              :    USE input_section_types,             ONLY: section_vals_get,&
      14              :                                               section_vals_get_subs_vals,&
      15              :                                               section_vals_type,&
      16              :                                               section_vals_val_get
      17              :    USE kinds,                           ONLY: dp
      18              :    USE mathconstants,                   ONLY: pi
      19              : #include "./base/base_uses.f90"
      20              : 
      21              :    IMPLICIT NONE
      22              : 
      23              :    PRIVATE
      24              : 
      25              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_dos_utils'
      26              : 
      27              :    INTEGER, PARAMETER, PUBLIC :: broadening_gaussian = 1, &
      28              :                                  broadening_lorentzian = 2, &
      29              :                                  broadening_pseudo_voigt = 3
      30              :    INTEGER, PARAMETER, PUBLIC :: dos_energy_unit_hartree = 1, &
      31              :                                  dos_energy_unit_ev = 2
      32              :    INTEGER, PARAMETER, PUBLIC :: dos_energy_zero_auto = 1, &
      33              :                                  dos_energy_zero_absolute = 2, &
      34              :                                  dos_energy_zero_fermi = 3, &
      35              :                                  dos_energy_zero_hoco = 4
      36              : 
      37              :    PUBLIC :: add_broadened_peak, add_broadened_value, broadening_cutoff, broadening_function, &
      38              :              dos_density_scale, dos_energy_label, dos_energy_scale, dos_energy_zero_label, &
      39              :              dos_resolve_energy_zero, get_dos_pdos_flags, write_broadening_info
      40              : 
      41              : CONTAINS
      42              : 
      43              : ! **************************************************************************************************
      44              : !> \brief Return the conversion factor from internal energy units to the selected DOS energy unit.
      45              : !> \param energy_unit ...
      46              : !> \return ...
      47              : ! **************************************************************************************************
      48          632 :    FUNCTION dos_energy_scale(energy_unit) RESULT(scale)
      49              : 
      50              :       INTEGER, INTENT(IN)                                :: energy_unit
      51              :       REAL(KIND=dp)                                      :: scale
      52              : 
      53          854 :       SELECT CASE (energy_unit)
      54              :       CASE (dos_energy_unit_ev)
      55          222 :          scale = cp_unit_from_cp2k(value=1.0_dp, unit_str="eV")
      56              :       CASE DEFAULT
      57          632 :          scale = 1.0_dp
      58              :       END SELECT
      59              : 
      60          632 :    END FUNCTION dos_energy_scale
      61              : 
      62              : ! **************************************************************************************************
      63              : !> \brief Return the DOS-density conversion factor for the selected energy unit.
      64              : !> \param energy_unit ...
      65              : !> \return ...
      66              : ! **************************************************************************************************
      67          188 :    FUNCTION dos_density_scale(energy_unit) RESULT(scale)
      68              : 
      69              :       INTEGER, INTENT(IN)                                :: energy_unit
      70              :       REAL(KIND=dp)                                      :: scale
      71              : 
      72          188 :       scale = 1.0_dp/dos_energy_scale(energy_unit)
      73              : 
      74          188 :    END FUNCTION dos_density_scale
      75              : 
      76              : ! **************************************************************************************************
      77              : !> \brief Return the energy-column label for DOS-like output.
      78              : !> \param energy_unit ...
      79              : !> \return ...
      80              : ! **************************************************************************************************
      81          222 :    FUNCTION dos_energy_label(energy_unit) RESULT(label)
      82              : 
      83              :       INTEGER, INTENT(IN)                                :: energy_unit
      84              :       CHARACTER(LEN=16)                                  :: label
      85              : 
      86          222 :       SELECT CASE (energy_unit)
      87              :       CASE (dos_energy_unit_ev)
      88            0 :          label = "Energy[eV]"
      89              :       CASE DEFAULT
      90          222 :          label = "Energy[a.u.]"
      91              :       END SELECT
      92              : 
      93          222 :    END FUNCTION dos_energy_label
      94              : 
      95              : ! **************************************************************************************************
      96              : !> \brief Return the label for the selected DOS energy zero.
      97              : !> \param energy_zero ...
      98              : !> \return ...
      99              : ! **************************************************************************************************
     100          136 :    FUNCTION dos_energy_zero_label(energy_zero) RESULT(label)
     101              : 
     102              :       INTEGER, INTENT(IN)                                :: energy_zero
     103              :       CHARACTER(LEN=16)                                  :: label
     104              : 
     105          136 :       SELECT CASE (energy_zero)
     106              :       CASE (dos_energy_zero_absolute)
     107            0 :          label = "ABSOLUTE"
     108              :       CASE (dos_energy_zero_hoco)
     109          100 :          label = "HOCO"
     110              :       CASE (dos_energy_zero_auto)
     111            0 :          label = "AUTO"
     112              :       CASE DEFAULT
     113          136 :          label = "FERMI"
     114              :       END SELECT
     115              : 
     116          136 :    END FUNCTION dos_energy_zero_label
     117              : 
     118              : ! **************************************************************************************************
     119              : !> \brief Resolve AUTO energy-zero selection for DOS-like output.
     120              : !> \param energy_zero ...
     121              : !> \param smearing_enabled ...
     122              : !> \param fractional_occupation ...
     123              : !> \return ...
     124              : ! **************************************************************************************************
     125          136 :    FUNCTION dos_resolve_energy_zero(energy_zero, smearing_enabled, fractional_occupation) RESULT(resolved)
     126              : 
     127              :       INTEGER, INTENT(IN)                                :: energy_zero
     128              :       LOGICAL, INTENT(IN)                                :: smearing_enabled, fractional_occupation
     129              :       INTEGER                                            :: resolved
     130              : 
     131          136 :       IF (energy_zero == dos_energy_zero_auto) THEN
     132          136 :          IF (smearing_enabled .OR. fractional_occupation) THEN
     133              :             resolved = dos_energy_zero_fermi
     134              :          ELSE
     135          100 :             resolved = dos_energy_zero_hoco
     136              :          END IF
     137              :       ELSE
     138              :          resolved = energy_zero
     139              :       END IF
     140              : 
     141          136 :    END FUNCTION dos_resolve_energy_zero
     142              : 
     143              : ! **************************************************************************************************
     144              : !> \brief Resolve projected-DOS requests from a DOS print section.
     145              : !> \param dos_section DOS print section
     146              : !> \param do_dos_output whether the DOS print key is active
     147              : !> \param do_projected_dos whether any projected DOS output is requested
     148              : !> \param do_pdos whether kind-resolved PDOS output is requested
     149              : !> \param do_pdos_raw whether raw kind-resolved PDOS output is requested
     150              : ! **************************************************************************************************
     151        22185 :    SUBROUTINE get_dos_pdos_flags(dos_section, do_dos_output, do_projected_dos, do_pdos, do_pdos_raw)
     152              : 
     153              :       TYPE(section_vals_type), POINTER                   :: dos_section
     154              :       LOGICAL, INTENT(IN)                                :: do_dos_output
     155              :       LOGICAL, INTENT(OUT)                               :: do_projected_dos, do_pdos, do_pdos_raw
     156              : 
     157              :       INTEGER                                            :: nrep
     158              :       LOGICAL                                            :: has_ldos, has_r_ldos
     159              :       TYPE(section_vals_type), POINTER                   :: ldos_section, pdos_section
     160              : 
     161        22185 :       do_projected_dos = .FALSE.
     162        22185 :       do_pdos = .FALSE.
     163        22185 :       do_pdos_raw = .FALSE.
     164        22185 :       has_ldos = .FALSE.
     165        22185 :       has_r_ldos = .FALSE.
     166              : 
     167        22185 :       IF (do_dos_output) THEN
     168           86 :          pdos_section => section_vals_get_subs_vals(dos_section, "PDOS")
     169           86 :          CALL section_vals_get(pdos_section, explicit=do_pdos)
     170           86 :          IF (do_pdos) CALL section_vals_val_get(pdos_section, "RAW", l_val=do_pdos_raw)
     171           86 :          ldos_section => section_vals_get_subs_vals(dos_section, "LDOS")
     172           86 :          CALL section_vals_get(ldos_section, n_repetition=nrep)
     173           86 :          has_ldos = (nrep > 0)
     174           86 :          ldos_section => section_vals_get_subs_vals(dos_section, "R_LDOS")
     175           86 :          CALL section_vals_get(ldos_section, n_repetition=nrep)
     176           86 :          has_r_ldos = (nrep > 0)
     177              :       END IF
     178              : 
     179        22185 :       do_projected_dos = do_pdos .OR. has_ldos .OR. has_r_ldos
     180              : 
     181        22185 :    END SUBROUTINE get_dos_pdos_flags
     182              : 
     183              : ! **************************************************************************************************
     184              : !> \brief Add a broadened spectral line to a DOS curve.
     185              : !> \param dos ...
     186              : !> \param occ_dos ...
     187              : !> \param emin ...
     188              : !> \param de ...
     189              : !> \param eig ...
     190              : !> \param occ ...
     191              : !> \param weight ...
     192              : !> \param broaden_type ...
     193              : !> \param broaden_width ...
     194              : !> \param voigt_mixing ...
     195              : ! **************************************************************************************************
     196         2808 :    SUBROUTINE add_broadened_peak(dos, occ_dos, emin, de, eig, occ, weight, broaden_type, &
     197              :                                  broaden_width, voigt_mixing)
     198              : 
     199              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: dos, occ_dos
     200              :       REAL(KIND=dp), INTENT(IN)                          :: emin, de, eig, occ, weight
     201              :       INTEGER, INTENT(IN)                                :: broaden_type
     202              :       REAL(KIND=dp), INTENT(IN)                          :: broaden_width, voigt_mixing
     203              : 
     204              :       INTEGER                                            :: i, iend, istart, nhist
     205              :       REAL(KIND=dp)                                      :: eval, line_shape
     206              : 
     207         2808 :       nhist = SIZE(dos)
     208         2808 :       istart = MAX(1, FLOOR((eig - broadening_cutoff(broaden_type, broaden_width) - emin)/de) + 1)
     209         2808 :       iend = MIN(nhist, CEILING((eig + broadening_cutoff(broaden_type, broaden_width) - emin)/de) + 1)
     210        72841 :       DO i = istart, iend
     211        70033 :          eval = emin + (i - 1)*de
     212        70033 :          line_shape = broadening_function(eval - eig, broaden_type, broaden_width, voigt_mixing)
     213        70033 :          dos(i) = dos(i) + weight*line_shape
     214        72841 :          occ_dos(i) = occ_dos(i) + weight*occ*line_shape
     215              :       END DO
     216              : 
     217         2808 :    END SUBROUTINE add_broadened_peak
     218              : 
     219              : ! **************************************************************************************************
     220              : !> \brief Add a broadened spectral line with a scalar weight to a curve.
     221              : !> \param curve ...
     222              : !> \param emin ...
     223              : !> \param de ...
     224              : !> \param eig ...
     225              : !> \param weight ...
     226              : !> \param broaden_type ...
     227              : !> \param broaden_width ...
     228              : !> \param voigt_mixing ...
     229              : ! **************************************************************************************************
     230         5460 :    SUBROUTINE add_broadened_value(curve, emin, de, eig, weight, broaden_type, &
     231              :                                   broaden_width, voigt_mixing)
     232              : 
     233              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: curve
     234              :       REAL(KIND=dp), INTENT(IN)                          :: emin, de, eig, weight
     235              :       INTEGER, INTENT(IN)                                :: broaden_type
     236              :       REAL(KIND=dp), INTENT(IN)                          :: broaden_width, voigt_mixing
     237              : 
     238              :       INTEGER                                            :: i, iend, istart, nhist
     239              :       REAL(KIND=dp)                                      :: eval, line_shape
     240              : 
     241         5460 :       nhist = SIZE(curve)
     242         5460 :       istart = MAX(1, FLOOR((eig - broadening_cutoff(broaden_type, broaden_width) - emin)/de) + 1)
     243         5460 :       iend = MIN(nhist, CEILING((eig + broadening_cutoff(broaden_type, broaden_width) - emin)/de) + 1)
     244        57428 :       DO i = istart, iend
     245        51968 :          eval = emin + (i - 1)*de
     246        51968 :          line_shape = broadening_function(eval - eig, broaden_type, broaden_width, voigt_mixing)
     247        57428 :          curve(i) = curve(i) + weight*line_shape
     248              :       END DO
     249              : 
     250         5460 :    END SUBROUTINE add_broadened_value
     251              : 
     252              : ! **************************************************************************************************
     253              : !> \brief Broadening cutoff used for numerical accumulation.
     254              : !> \param broaden_type ...
     255              : !> \param broaden_width ...
     256              : !> \return ...
     257              : ! **************************************************************************************************
     258         8456 :    PURE FUNCTION broadening_cutoff(broaden_type, broaden_width) RESULT(cutoff)
     259              : 
     260              :       INTEGER, INTENT(IN)                                :: broaden_type
     261              :       REAL(KIND=dp), INTENT(IN)                          :: broaden_width
     262              :       REAL(KIND=dp)                                      :: cutoff
     263              : 
     264         8456 :       IF (broaden_type == broadening_gaussian) THEN
     265         8456 :          cutoff = 8.0_dp*broaden_width
     266              :       ELSE
     267            0 :          cutoff = 50.0_dp*broaden_width
     268              :       END IF
     269              : 
     270         8456 :    END FUNCTION broadening_cutoff
     271              : 
     272              : ! **************************************************************************************************
     273              : !> \brief Normalized broadening function. BROADEN_WIDTH is FWHM.
     274              : !> \param delta_e ...
     275              : !> \param broaden_type ...
     276              : !> \param broaden_width ...
     277              : !> \param voigt_mixing ...
     278              : !> \return ...
     279              : ! **************************************************************************************************
     280       152009 :    PURE FUNCTION broadening_function(delta_e, broaden_type, broaden_width, voigt_mixing) RESULT(value)
     281              : 
     282              :       REAL(KIND=dp), INTENT(IN)                          :: delta_e
     283              :       INTEGER, INTENT(IN)                                :: broaden_type
     284              :       REAL(KIND=dp), INTENT(IN)                          :: broaden_width, voigt_mixing
     285              :       REAL(KIND=dp)                                      :: value
     286              : 
     287              :       REAL(KIND=dp)                                      :: eta, gamma, gaussian_value, sigma
     288              : 
     289       152009 :       IF (broaden_width <= 0.0_dp) THEN
     290       152009 :          value = 0.0_dp
     291              :          RETURN
     292              :       END IF
     293              : 
     294       152009 :       sigma = broaden_width/(2.0_dp*SQRT(2.0_dp*LOG(2.0_dp)))
     295       152009 :       gamma = 0.5_dp*broaden_width
     296       152009 :       gaussian_value = EXP(-0.5_dp*(delta_e/sigma)**2)/(sigma*SQRT(2.0_dp*pi))
     297              : 
     298       152009 :       SELECT CASE (broaden_type)
     299              :       CASE (broadening_gaussian)
     300            0 :          value = gaussian_value
     301              :       CASE (broadening_lorentzian)
     302            0 :          value = gamma/(pi*(delta_e**2 + gamma**2))
     303              :       CASE (broadening_pseudo_voigt)
     304            0 :          eta = MIN(1.0_dp, MAX(0.0_dp, voigt_mixing))
     305            0 :          value = eta*gamma/(pi*(delta_e**2 + gamma**2)) + (1.0_dp - eta)*gaussian_value
     306              :       CASE DEFAULT
     307       152009 :          value = gaussian_value
     308              :       END SELECT
     309              : 
     310              :    END FUNCTION broadening_function
     311              : 
     312              : ! **************************************************************************************************
     313              : !> \brief Write broadening metadata.
     314              : !> \param iw ...
     315              : !> \param broaden_type ...
     316              : !> \param broaden_width ...
     317              : !> \param voigt_mixing ...
     318              : ! **************************************************************************************************
     319           94 :    SUBROUTINE write_broadening_info(iw, broaden_type, broaden_width, voigt_mixing)
     320              : 
     321              :       INTEGER, INTENT(IN)                                :: iw, broaden_type
     322              :       REAL(KIND=dp), INTENT(IN)                          :: broaden_width, voigt_mixing
     323              : 
     324              :       REAL(KIND=dp)                                      :: broaden_width_ev
     325              : 
     326           94 :       broaden_width_ev = cp_unit_from_cp2k(value=broaden_width, unit_str="eV")
     327              : 
     328          188 :       SELECT CASE (broaden_type)
     329              :       CASE (broadening_gaussian)
     330              :          WRITE (UNIT=iw, FMT="(A,F10.8,A,F8.6,A)") &
     331           94 :             "# Gaussian broadening, FWHM = ", broaden_width, " a.u. = ", broaden_width_ev, " eV"
     332              :       CASE (broadening_lorentzian)
     333              :          WRITE (UNIT=iw, FMT="(A,F10.8,A,F8.6,A)") &
     334            0 :             "# Lorentzian broadening, FWHM = ", broaden_width, " a.u. = ", broaden_width_ev, " eV"
     335              :       CASE (broadening_pseudo_voigt)
     336              :          WRITE (UNIT=iw, FMT="(A,F10.8,A,F8.6,A,F8.4)") &
     337            0 :             "# Pseudo-Voigt broadening, FWHM = ", broaden_width, " a.u. = ", &
     338           94 :             broaden_width_ev, " eV, Lorentzian fraction = ", MIN(1.0_dp, MAX(0.0_dp, voigt_mixing))
     339              :       END SELECT
     340              : 
     341           94 :    END SUBROUTINE write_broadening_info
     342              : 
     343              : END MODULE qs_dos_utils
        

Generated by: LCOV version 2.0-1