LCOV - code coverage report
Current view: top level - src - greenx_interface.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:92574dc) Lines: 48.0 % 100 48
Test Date: 2026-09-24 01:27:39 Functions: 50.0 % 4 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 Interface to the Greenx library
      10              : !> \par History
      11              : !>      07.2025 Refactored from RPA and BSE modules [Frederick Stein]
      12              : ! **************************************************************************************************
      13              : MODULE greenx_interface
      14              :    USE kinds, ONLY: dp
      15              :    USE cp_log_handling, ONLY: cp_logger_type, &
      16              :                               cp_get_default_logger, &
      17              :                               cp_logger_get_default_io_unit
      18              :    USE cp_output_handling, ONLY: cp_print_key_unit_nr, &
      19              :                                  cp_print_key_finished_output, &
      20              :                                  cp_print_key_generate_filename, &
      21              :                                  low_print_level, &
      22              :                                  medium_print_level
      23              :    USE input_section_types, ONLY: section_vals_type
      24              :    USE machine, ONLY: m_flush
      25              :    USE physcon, ONLY: evolt
      26              : #if defined (__GREENX)
      27              :    USE gx_ac, ONLY: create_thiele_pade, &
      28              :                     evaluate_thiele_pade_at, &
      29              :                     free_params, &
      30              :                     params
      31              :    USE gx_minimax, ONLY: gx_minimax_grid
      32              : #endif
      33              : 
      34              : #include "./base/base_uses.f90"
      35              : 
      36              :    IMPLICIT NONE
      37              : 
      38              :    PRIVATE
      39              : 
      40              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'greenx_interface'
      41              : 
      42              :    PUBLIC :: greenx_get_minimax_grid, greenx_refine_pade, greenx_output_polarizability, greenx_refine_ft
      43              : 
      44              : CONTAINS
      45              : 
      46              : ! **************************************************************************************************
      47              : !> \brief Get a minimax grid from GreenX when it is available.
      48              : !> \param unit_nr Output unit for diagnostics.
      49              : !> \param num_integ_points Number of minimax points.
      50              : !> \param emin Lower energy bound.
      51              : !> \param emax Upper energy bound.
      52              : !> \param regularization_minimax Regularization of the minimax fit.
      53              : !> \param imaginary_time Imaginary-time grid points.
      54              : !> \param time_weights_at_zero_frequency Imaginary-time weights.
      55              : !> \param frequency Frequency grid points.
      56              : !> \param frequency_weights Frequency weights.
      57              : !> \param cosine_time_to_frequency_weights Cosine time-to-frequency weights.
      58              : !> \param cosine_frequency_to_time_weights Cosine frequency-to-time weights.
      59              : !> \param sine_time_to_frequency_weights Sine time-to-frequency weights.
      60              : !> \param ierr Zero if GreenX supplied a grid, nonzero otherwise.
      61              : ! **************************************************************************************************
      62          214 :    SUBROUTINE greenx_get_minimax_grid(unit_nr, num_integ_points, emin, emax, regularization_minimax, &
      63              :                                       imaginary_time, time_weights_at_zero_frequency, frequency, &
      64              :                                       frequency_weights, cosine_time_to_frequency_weights, &
      65              :                                       cosine_frequency_to_time_weights, sine_time_to_frequency_weights, ierr)
      66              : 
      67              :       INTEGER, INTENT(IN)                                :: unit_nr, num_integ_points
      68              :       REAL(KIND=dp), INTENT(IN)                           :: emin, emax, regularization_minimax
      69              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: imaginary_time, &
      70              :                                                                time_weights_at_zero_frequency, &
      71              :                                                                frequency, frequency_weights
      72              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), INTENT(OUT) :: cosine_time_to_frequency_weights, &
      73              :                                                                   cosine_frequency_to_time_weights, &
      74              :                                                                   sine_time_to_frequency_weights
      75              :       INTEGER, INTENT(OUT)                                :: ierr
      76              : 
      77              : #if defined (__GREENX)
      78              :       INTEGER                                            :: gi
      79              :       REAL(KIND=dp)                                      :: cosft_duality_error_greenx, &
      80              :                                                             max_errors_greenx(3)
      81              : 
      82              :       CALL gx_minimax_grid(num_integ_points, emin, emax, imaginary_time, &
      83              :                            time_weights_at_zero_frequency, frequency, frequency_weights, &
      84              :                            cosine_time_to_frequency_weights, cosine_frequency_to_time_weights, &
      85              :                            sine_time_to_frequency_weights, max_errors_greenx, cosft_duality_error_greenx, ierr, &
      86          214 :                            bare_cos_sin_weights=.TRUE., regularization=regularization_minimax)
      87          214 :       IF (ierr == 0) THEN
      88              :          ! Factor 4 is hard-coded in the RPA weights in the internal CP2K minimax routines
      89          878 :          frequency_weights(:) = frequency_weights(:)*4.0_dp
      90           78 :          IF (unit_nr > 0) THEN
      91              :             WRITE (UNIT=unit_nr, FMT="(T3,A,T75,i6)") &
      92           39 :                "GREENX MINIMAX_INFO| Number of integration points:", num_integ_points
      93              :             WRITE (UNIT=unit_nr, FMT="(T3,A,T61,3F20.7)") &
      94           39 :                "GREENX MINIMAX_INFO| Gap (Emin):", emin
      95              :             WRITE (UNIT=unit_nr, FMT="(T3,A,T61,3F20.7)") &
      96           39 :                "GREENX MINIMAX_INFO| Maximum eigenvalue difference (Emax):", emax
      97              :             WRITE (UNIT=unit_nr, FMT="(T3,A,T61,3F20.4)") &
      98           39 :                "GREENX MINIMAX_INFO| Energy range (Emax/Emin):", emax/emin
      99              :             WRITE (UNIT=unit_nr, FMT="(T3,A,T54,A,T72,A)") &
     100           39 :                "GREENX MINIMAX_INFO| Frequency grid (scaled):", "Weights", "Abscissas"
     101          439 :             DO gi = 1, num_integ_points
     102          439 :                WRITE (UNIT=unit_nr, FMT="(T41,F20.10,F20.10)") frequency_weights(gi), frequency(gi)
     103              :             END DO
     104              :             WRITE (UNIT=unit_nr, FMT="(T3,A,T54,A,T72,A)") &
     105           39 :                "GREENX MINIMAX_INFO| Time grid (scaled):", "Weights", "Abscissas"
     106          439 :             DO gi = 1, num_integ_points
     107              :                WRITE (UNIT=unit_nr, FMT="(T41,F20.10,F20.10)") &
     108          439 :                   time_weights_at_zero_frequency(gi), imaginary_time(gi)
     109              :             END DO
     110           39 :             CALL m_flush(unit_nr)
     111              :          END IF
     112              :       ELSE
     113          136 :          IF (unit_nr > 0) THEN
     114              :             WRITE (UNIT=unit_nr, FMT="(T3,A,T75)") &
     115           68 :                "GREENX MINIMAX_INFO| Grid not available, use internal CP2K grids"
     116           68 :             CALL m_flush(unit_nr)
     117              :          END IF
     118              :       END IF
     119              : #else
     120              :       ierr = 1
     121              :       MARK_USED(unit_nr)
     122              :       MARK_USED(num_integ_points)
     123              :       MARK_USED(emin)
     124              :       MARK_USED(emax)
     125              :       MARK_USED(regularization_minimax)
     126              :       MARK_USED(imaginary_time)
     127              :       MARK_USED(time_weights_at_zero_frequency)
     128              :       MARK_USED(frequency)
     129              :       MARK_USED(frequency_weights)
     130              :       MARK_USED(cosine_time_to_frequency_weights)
     131              :       MARK_USED(cosine_frequency_to_time_weights)
     132              :       MARK_USED(sine_time_to_frequency_weights)
     133              : #endif
     134              : 
     135          214 :    END SUBROUTINE greenx_get_minimax_grid
     136              : 
     137              : ! **************************************************************************************************
     138              : !> \brief Refines Pade approximants using GreenX, skips this step if GreenX is not available
     139              : !> \param e_min ...
     140              : !> \param e_max ...
     141              : !> \param x_eval ...
     142              : !> \param number_of_simulation_steps ...
     143              : !> \param number_of_pade_points ...
     144              : !> \param logger ...
     145              : !> \param ft_section ...
     146              : !> \param bse_unit ...
     147              : !> \param omega_series ...
     148              : !> \param ft_full_series ...
     149              : ! **************************************************************************************************
     150            0 :    SUBROUTINE greenx_refine_pade(e_min, e_max, x_eval, number_of_simulation_steps, number_of_pade_points, &
     151            0 :                                  logger, ft_section, bse_unit, omega_series, ft_full_series)
     152              :       REAL(KIND=dp), INTENT(IN) :: e_min, e_max
     153              :       COMPLEX(KIND=dp), DIMENSION(:), POINTER :: x_eval
     154              :       INTEGER, INTENT(IN) :: number_of_simulation_steps, number_of_pade_points
     155              :       TYPE(cp_logger_type), POINTER                      :: logger
     156              :       TYPE(section_vals_type), POINTER                          :: ft_section
     157              :       INTEGER, INTENT(IN) :: bse_unit
     158              :       REAL(KIND=dp), DIMENSION(number_of_simulation_steps + 2), INTENT(INOUT) :: omega_series
     159              :       REAL(KIND=dp), DIMENSION(6, number_of_simulation_steps + 2), INTENT(INOUT) :: ft_full_series
     160              : #if defined (__GREENX)
     161              :       INTEGER                                             :: i, ft_unit
     162            0 :       COMPLEX(kind=dp), DIMENSION(:), ALLOCATABLE         :: omega_complex, &
     163            0 :                                                              moments_ft_complex
     164            0 :       COMPLEX(kind=dp), DIMENSION(:, :), ALLOCATABLE      :: moments_eval_complex
     165              : 
     166              :       ! Report Padé refinement
     167            0 :       IF (bse_unit > 0) WRITE (bse_unit, '(A10,A27,E23.8E3,E20.8E3)') &
     168            0 :          " PADE_FT| ", "Evaluation grid bounds [eV]", e_min, e_max
     169            0 :       ALLOCATE (omega_complex(number_of_simulation_steps + 2))
     170            0 :       ALLOCATE (moments_ft_complex(number_of_simulation_steps + 2))
     171            0 :       ALLOCATE (moments_eval_complex(3, number_of_pade_points))
     172            0 :       omega_complex(:) = CMPLX(omega_series(:), 0.0, kind=dp)
     173            0 :       DO i = 1, 3
     174              :          moments_ft_complex(:) = CMPLX(ft_full_series(2*i - 1, :), &
     175              :                                        ft_full_series(2*i, :), &
     176            0 :                                        kind=dp)
     177              :          ! Copy the fitting parameters
     178              :          ! TODO : Optional direct setting of parameters?
     179            0 :          CALL greenx_refine_ft(e_min, e_max, omega_complex, moments_ft_complex, x_eval, moments_eval_complex(i, :))
     180              :       END DO
     181              :       ! Write into alternative file
     182              :       ft_unit = cp_print_key_unit_nr(logger, ft_section, extension="_PADE.dat", &
     183            0 :                                      file_form="FORMATTED", file_position="REWIND")
     184            0 :       IF (ft_unit > 0) THEN
     185            0 :          DO i = 1, number_of_pade_points
     186              :             WRITE (ft_unit, '(E20.8E3,E20.8E3,E20.8E3,E20.8E3,E20.8E3,E20.8E3,E20.8E3)') &
     187            0 :                REAL(x_eval(i)), REAL(moments_eval_complex(1, i)), AIMAG(moments_eval_complex(1, i)), &
     188            0 :                REAL(moments_eval_complex(2, i)), AIMAG(moments_eval_complex(2, i)), &
     189            0 :                REAL(moments_eval_complex(3, i)), AIMAG(moments_eval_complex(3, i))
     190              :          END DO
     191              :       END IF
     192            0 :       CALL cp_print_key_finished_output(ft_unit, logger, ft_section)
     193            0 :       DEALLOCATE (omega_complex)
     194            0 :       DEALLOCATE (moments_ft_complex)
     195            0 :       DEALLOCATE (moments_eval_complex)
     196              : #else
     197              :       IF (bse_unit > 0) WRITE (bse_unit, '(A10,A70)') &
     198              :          " PADE_FT| ", "GreenX library is not available. Refinement is skipped"
     199              :       MARK_USED(e_min)
     200              :       MARK_USED(e_max)
     201              :       MARK_USED(x_eval)
     202              :       MARK_USED(number_of_simulation_steps)
     203              :       MARK_USED(number_of_pade_points)
     204              :       MARK_USED(logger)
     205              :       MARK_USED(ft_section)
     206              :       MARK_USED(omega_series)
     207              :       MARK_USED(ft_full_series)
     208              : #endif
     209            0 :    END SUBROUTINE greenx_refine_pade
     210              : ! **************************************************************************************************
     211              : !> \brief Outputs the isotropic polarizability tensor element alpha _ ij = mu_i(omega)/E_j(omega),
     212              : !>        where i and j are provided by the configuration. The tensor element is energy dependent and
     213              : !>        has real and imaginary parts
     214              : !> \param logger ...
     215              : !> \param pol_section ...
     216              : !> \param bse_unit ...
     217              : !> \param pol_elements ...
     218              : !> \param x_eval ...
     219              : !> \param polarizability_refined ...
     220              : ! **************************************************************************************************
     221            0 :    SUBROUTINE greenx_output_polarizability(logger, pol_section, bse_unit, pol_elements, x_eval, polarizability_refined)
     222              :       TYPE(cp_logger_type), POINTER                      :: logger
     223              :       TYPE(section_vals_type), POINTER                          :: pol_section
     224              :       INTEGER, INTENT(IN) :: bse_unit
     225              :       INTEGER, DIMENSION(:, :), POINTER                         :: pol_elements
     226              :       COMPLEX(KIND=dp), DIMENSION(:), POINTER :: x_eval
     227              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN)     :: polarizability_refined
     228              : #if defined(__GREENX)
     229              :       INTEGER                                            :: pol_unit, &
     230              :                                                             i, k, n_elems
     231              : 
     232            0 :       n_elems = SIZE(pol_elements, 1)
     233              :       ! Print out the refined polarizability to a file
     234              :       pol_unit = cp_print_key_unit_nr(logger, pol_section, extension="_PADE.dat", &
     235            0 :                                       file_form="FORMATTED", file_position="REWIND")
     236              :       ! Printing for both the stdout and separate file
     237            0 :       IF (pol_unit > 0) THEN
     238            0 :          IF (pol_unit == bse_unit) THEN
     239              :             ! Print the stdout preline
     240            0 :             WRITE (pol_unit, '(A21)', advance="no") " POLARIZABILITY_PADE|"
     241              :          ELSE
     242              :             ! Print also the energy in atomic units
     243            0 :             WRITE (pol_unit, '(A1,A19)', advance="no") "#", "omega [a.u.]"
     244              :          END IF
     245              :          ! Common - print the energy in eV
     246            0 :          WRITE (pol_unit, '(A20)', advance="no") "Energy [eV]"
     247              :          ! Print a header for each polarizability element
     248            0 :          DO k = 1, n_elems - 1
     249              :             WRITE (pol_unit, '(A16,I2,I2,A16,I2,I2)', advance="no") &
     250            0 :                "Real pol.", pol_elements(k, 1), pol_elements(k, 2), &
     251            0 :                "Imag pol.", pol_elements(k, 1), pol_elements(k, 2)
     252              :          END DO
     253              :          WRITE (pol_unit, '(A16,I2,I2,A16,I2,I2)') &
     254            0 :             "Real pol.", pol_elements(n_elems, 1), pol_elements(n_elems, 2), &
     255            0 :             "Imag pol.", pol_elements(n_elems, 1), pol_elements(n_elems, 2)
     256            0 :          DO i = 1, SIZE(x_eval)
     257            0 :             IF (pol_unit == bse_unit) THEN
     258              :                ! Print the stdout preline
     259            0 :                WRITE (pol_unit, '(A21)', advance="no") " POLARIZABILITY_PADE|"
     260              :             ELSE
     261              :                ! omega in a.u.
     262            0 :                WRITE (pol_unit, '(E20.8E3)', advance="no") REAL(x_eval(i), kind=dp)
     263              :             END IF
     264              :             ! Common values
     265            0 :             WRITE (pol_unit, '(E20.8E3)', advance="no") REAL(x_eval(i), kind=dp)*evolt
     266            0 :             DO k = 1, n_elems - 1
     267              :                WRITE (pol_unit, '(E20.8E3,E20.8E3)', advance="no") &
     268            0 :                   REAL(polarizability_refined(k, i)), AIMAG(polarizability_refined(k, i))
     269              :             END DO
     270              :             ! Print the final value and advance
     271              :             WRITE (pol_unit, '(E20.8E3,E20.8E3)') &
     272            0 :                REAL(polarizability_refined(n_elems, i)), AIMAG(polarizability_refined(n_elems, i))
     273              :          END DO
     274            0 :          CALL cp_print_key_finished_output(pol_unit, logger, pol_section)
     275              :       END IF
     276              : #else
     277              :       MARK_USED(logger)
     278              :       MARK_USED(pol_section)
     279              :       MARK_USED(bse_unit)
     280              :       MARK_USED(pol_elements)
     281              :       MARK_USED(x_eval)
     282              :       MARK_USED(polarizability_refined)
     283              : #endif
     284            0 :    END SUBROUTINE greenx_output_polarizability
     285              : ! **************************************************************************************************
     286              : !> \brief Refines the FT grid using Padé approximants
     287              : !> \param fit_e_min ...
     288              : !> \param fit_e_max ...
     289              : !> \param x_fit Input x-variables
     290              : !> \param y_fit Input y-variables
     291              : !> \param x_eval Refined x-variables
     292              : !> \param y_eval Refined y-variables
     293              : !> \param n_pade_opt ...
     294              : ! **************************************************************************************************
     295            6 :    SUBROUTINE greenx_refine_ft(fit_e_min, fit_e_max, x_fit, y_fit, x_eval, y_eval, n_pade_opt)
     296              :       REAL(kind=dp)                                      :: fit_e_min, &
     297              :                                                             fit_e_max
     298              :       COMPLEX(kind=dp), DIMENSION(:)                     :: x_fit, &
     299              :                                                             y_fit, &
     300              :                                                             x_eval, &
     301              :                                                             y_eval
     302              :       INTEGER, OPTIONAL                                  :: n_pade_opt
     303              : #if defined (__GREENX)
     304              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'greenx_refine_ft'
     305              : 
     306              :       INTEGER                                            :: fit_start, &
     307              :                                                             fit_end, &
     308              :                                                             max_fit, &
     309              :                                                             n_fit, &
     310              :                                                             n_pade, &
     311              :                                                             n_eval, &
     312              :                                                             i, &
     313              :                                                             handle, &
     314              :                                                             unit_nr
     315              :       TYPE(cp_logger_type), POINTER                      :: logger
     316              :       TYPE(params)                                       :: pade_params
     317              : 
     318            6 :       CALL timeset(routineN, handle)
     319              : 
     320              :       ! Get the sizes from arrays
     321            6 :       max_fit = SIZE(x_fit)
     322            6 :       n_eval = SIZE(x_eval)
     323              : 
     324              :       ! Search for the fit start and end indices
     325            6 :       fit_start = -1
     326            6 :       fit_end = -1
     327              :       ! Search for the subset of FT points which is within energy limits given by
     328              :       ! the input
     329              :       ! Do not search when automatic request of highest energy is made
     330            6 :       IF (fit_e_max < 0) fit_end = max_fit
     331          144 :       DO i = 1, max_fit
     332          144 :          IF (fit_start == -1 .AND. REAL(x_fit(i)) >= fit_e_min) fit_start = i
     333          144 :          IF (fit_end == -1 .AND. REAL(x_fit(i)) > fit_e_max) fit_end = i - 1
     334          144 :          IF (fit_start > 0 .AND. fit_end > 0) EXIT
     335              :       END DO
     336            6 :       IF (fit_start == -1) fit_start = 1
     337            6 :       IF (fit_end == -1) fit_end = max_fit
     338            6 :       n_fit = fit_end - fit_start + 1
     339              : 
     340            6 :       n_pade = n_fit/2
     341            6 :       IF (PRESENT(n_pade_opt)) n_pade = n_pade_opt
     342              : 
     343              :       ! Too few FT points (e.g. very short propagation with &FT on) leave n_pade < 1;
     344              :       ! the Thiele recurrence would then divide by zero. Skip, returning zeros.
     345            6 :       IF (n_pade < 1) THEN
     346            0 :          CPWARN("FT deck too short for Padé; raise STEPS or disable &FT.")
     347            0 :          y_eval(1:n_eval) = CMPLX(0.0, 0.0, kind=dp)
     348            0 :          CALL timestop(handle)
     349              :          RETURN
     350              :       END IF
     351              : 
     352              :       ! Warn about a large number of Padé parameters
     353            6 :       IF (n_pade > 1000) THEN
     354            0 :          CPWARN("More then 1000 Padé parameters requested - may reduce with FIT_E_MIN/FIT_E_MAX.")
     355              :       END IF
     356              : 
     357              :       ! The Padé order is derived from the FT bins inside [FIT_E_MIN, FIT_E_MAX]; report it so a
     358              :       ! spectrum's fit is reconstructable from the log. Distinct from the GW AC Padé (nparam_pade).
     359            6 :       logger => cp_get_default_logger()
     360            6 :       unit_nr = cp_logger_get_default_io_unit(logger)
     361            6 :       IF (unit_nr > 0) THEN
     362              :          WRITE (UNIT=unit_nr, FMT="(T3,A,T45,I6,I8,2F11.4)") &
     363            3 :             "GREENX FT_PADE| n_pade, n_fit, window [eV]", n_pade, n_fit, &
     364            6 :             REAL(x_fit(fit_start), kind=dp)*evolt, REAL(x_fit(fit_end), kind=dp)*evolt
     365              :       END IF
     366              : 
     367              :       ! TODO : Symmetry mode settable?
     368              :       ! Here, we assume that ft corresponds to transform of real trace
     369              :       pade_params = create_thiele_pade(n_pade, x_fit(fit_start:fit_end), y_fit(fit_start:fit_end), &
     370            6 :                                        enforce_symmetry="conjugate")
     371              : 
     372              :       ! Check whetner the splice is needed or not
     373         6000 :       y_eval(1:n_eval) = evaluate_thiele_pade_at(pade_params, x_eval)
     374              : 
     375            6 :       CALL free_params(pade_params)
     376            6 :       CALL timestop(handle)
     377              : #else
     378              :       ! Mark used
     379              :       MARK_USED(fit_e_min)
     380              :       MARK_USED(fit_e_max)
     381              :       MARK_USED(x_fit)
     382              :       MARK_USED(y_fit)
     383              :       MARK_USED(x_eval)
     384              :       MARK_USED(y_eval)
     385              :       MARK_USED(n_pade_opt)
     386              :       CPABORT("Calls to GreenX require CP2K to be compiled with support for GreenX.")
     387              : #endif
     388            6 :    END SUBROUTINE greenx_refine_ft
     389              : 
     390              : END MODULE greenx_interface
        

Generated by: LCOV version 2.0-1