LCOV - code coverage report
Current view: top level - src - floquet_main.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:21ef868) Lines: 100.0 % 43 43
Test Date: 2026-08-14 07:04:57 Functions: 100.0 % 1 1

            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 Floquet-Bloch band-structure calculations: builds and diagonalises the
      10              : !>        truncated Floquet-Bloch Hamiltonian H_F(k) for each DOS k-point and writes the m=0
      11              : !>        band structure, the quasi-energies and the Floquet density of states.
      12              : !> \par History
      13              : !> \author Shridhar Shanbhag (27.01.2026)
      14              : ! **************************************************************************************************
      15              : MODULE floquet_main
      16              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_release,&
      17              :                                               cp_blacs_env_type
      18              :    USE cp_cfm_diag,                     ONLY: cp_cfm_heevd
      19              :    USE cp_cfm_types,                    ONLY: cp_cfm_create,&
      20              :                                               cp_cfm_release,&
      21              :                                               cp_cfm_set_all,&
      22              :                                               cp_cfm_type
      23              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      24              :                                               cp_fm_struct_release,&
      25              :                                               cp_fm_struct_type
      26              :    USE floquet_types,                   ONLY: floquet_env_create,&
      27              :                                               floquet_env_release,&
      28              :                                               floquet_env_type
      29              :    USE floquet_utils,                   ONLY: &
      30              :         build_floquet_matrix, calculate_floquet_observables, check_floquet_convergence, &
      31              :         compute_e_k_de_dk_dipole, distribute_floquet_kp_data, floquet_sector_weights, &
      32              :         make_floquet_subgroups, write_floquet_header, write_floquet_results
      33              :    USE kinds,                           ONLY: dp
      34              :    USE mathconstants,                   ONLY: z_zero
      35              :    USE message_passing,                 ONLY: mp_para_env_release,&
      36              :                                               mp_para_env_type
      37              :    USE post_scf_bandstructure_types,    ONLY: post_scf_bandstructure_type
      38              :    USE qs_environment_types,            ONLY: get_qs_env,&
      39              :                                               qs_environment_type
      40              : #include "./base/base_uses.f90"
      41              : 
      42              :    IMPLICIT NONE
      43              : 
      44              :    PRIVATE
      45              : 
      46              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'floquet_main'
      47              : 
      48              :    PUBLIC :: floquet
      49              : 
      50              : CONTAINS
      51              : 
      52              : ! **************************************************************************************************
      53              : !> \brief Computes the Floquet-Bloch band structure, density of states and quasi-energies.
      54              : !> \param qs_env ...
      55              : !> \param bs_env ...
      56              : ! **************************************************************************************************
      57            2 :    SUBROUTINE floquet(qs_env, bs_env)
      58              :       TYPE(qs_environment_type), POINTER                 :: qs_env
      59              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
      60              : 
      61              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'floquet'
      62              : 
      63              :       COMPLEX(KIND=dp), ALLOCATABLE, &
      64            2 :          DIMENSION(:, :, :, :, :)                        :: dipole
      65              :       INTEGER                                            :: handle, ikp, ispin, my_group, &
      66              :                                                             n_est_done, n_source_done, ngroups
      67            2 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: group_distribution
      68            2 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :)     :: e_k
      69            2 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :, :)  :: de_dk
      70              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env_sub
      71              :       TYPE(cp_cfm_type)                                  :: cfm_eigenvectors, floquet_matrix
      72              :       TYPE(cp_fm_struct_type), POINTER                   :: floquet_struct
      73            2 :       TYPE(floquet_env_type)                             :: floquet_env
      74              :       TYPE(mp_para_env_type), POINTER                    :: para_env, para_env_sub
      75              : 
      76            2 :       CALL timeset(routineN, handle)
      77              : 
      78            2 :       CALL get_qs_env(qs_env, para_env=para_env)
      79              : 
      80              :       ! Floquet-Bloch theory: under a monochromatic electric field drive the Hamiltonian is periodic,
      81              :       ! H(k,t) = H(k,t+T) with T = 2π/Ω, where Ω is the angular frequency of the drive.
      82              :       ! By Floquet's theorem the solutions are:
      83              :       ! ψ_αk(t) = e^(-i λ_αk t) Σ_m e^(-i m Ω t) |F^m_αk>, with quasi-energies λ_αk and
      84              :       ! Fourier index m.
      85              :       ! In order to obtain the solutions λ_αk, we start with the Floquet Hamiltonian defined as,
      86              :       ! H_F(k,t) = H(k,t) - iħ d/dt.
      87              :       ! Taking the Fourier transform, the solutions are obtained by diagonalizing the matrix H_F(k)
      88              :       !   H_F(k) F_αk = λ_αk F_αk,
      89              :       ! where H_F(k) is block-tridiagonal in the sector index m = -M..M (M = MAX_FLOQUET_INDEX)
      90              :       ! and each block is nao×nao. We build and diagonalise H_F(k) per DOS k-point k and spin.
      91              :       ! The matrix H_F(k) has size (nao*(2*MAX_FLOQUET_INDEX+1) x nao*(2*MAX_FLOQUET_INDEX+1)).
      92              : 
      93            2 :       CALL write_floquet_header(bs_env)
      94              : 
      95            2 :       CALL floquet_env_create(floquet_env, bs_env)
      96              : 
      97              :       ! Equilibrium band data at every band-structure k-point k from H(k)C(k) = S(k)C(k)ε(k):
      98              :       !   band energies    ε_nk
      99              :       !   k-derivatives    ∇_k ε_nk = C_n(k)^† ∇_k H(k) C_n(k) - ε_nk C_n(k)^† ∇_k S(k) C_n(k)
     100              :       !   dipoles          d_nm(k) = <ψ_nk| r |ψ_mk>
     101              :       ! Results are distributed across ranks (k-point ikp stored on rank MOD(ikp-1,num_pe)).
     102            2 :       CALL compute_e_k_de_dk_dipole(qs_env, bs_env, e_k, de_dk, dipole)
     103              : 
     104              :       ! Split the global communicator into Floquet subgroups (Hamiltonian distributed within each
     105              :       ! subgroup, k-points distributed across subgroups) and create the subgroup BLACS context.
     106              :       CALL make_floquet_subgroups(qs_env, bs_env, para_env_sub, blacs_env_sub, &
     107            2 :                                   group_distribution, ngroups)
     108              : 
     109              :       !get inside make_floquet_subroutine
     110            2 :       my_group = group_distribution(para_env%mepos)
     111              : 
     112              :       CALL cp_fm_struct_create(floquet_struct, context=blacs_env_sub, para_env=para_env_sub, &
     113            2 :                                nrow_global=floquet_env%n_f_size, ncol_global=floquet_env%n_f_size)
     114            2 :       CALL cp_cfm_create(floquet_matrix, floquet_struct, set_zero=.TRUE.)
     115            2 :       CALL cp_cfm_create(cfm_eigenvectors, floquet_struct, set_zero=.TRUE.)
     116              : 
     117            2 :       n_source_done = 0
     118              : 
     119            4 :       DO ikp = 1, floquet_env%nkp_only_bs
     120              :          ! Only the subgroup that owns this k-point's data works on it
     121            2 :          IF (group_distribution(MOD(ikp - 1, para_env%num_pe)) /= my_group) CYCLE
     122              : 
     123            4 :          DO ispin = 1, floquet_env%n_spin
     124              :             ! Broadcast this k-point/spin's ε_nk, ∇_k ε_nk and d_nm(k) to every rank of the
     125              :             ! owning subgroup (its owner rank holds them from the distributed precompute above).
     126              :             CALL distribute_floquet_kp_data(para_env, para_env_sub, ispin, ikp, e_k, de_dk, &
     127            2 :                                             dipole, floquet_env)
     128              : 
     129              :             ! Assemble H_F(k) in Sambe space (blocks m,m' = -M..M, each nao×nao)
     130              :             !  diagonal      (H_F)^{m,m}_{nn'}   = (ε_nk + m ħΩ) δ_{nn'}
     131              :             !  off diagonal  (H_F)^{m,m±1}_{nn'} = Σ_a p^a_{nn'}(k) (i E_a e^{±iφ_a})/(2Ω),  a=x,y,z
     132              :             ! with momentum   p^a_{nn'}(k) = i d^a_{nn'}(k)(ε_nk - ε_n'k) + ∇_{k_a} ε_nk δ_{nn'}
     133              :             ! and field       E_a = |E| pol_a.
     134            2 :             CALL build_floquet_matrix(bs_env, floquet_env, floquet_matrix)
     135              : 
     136              :             ! Diagonalise H_F(k) F_αk = λ_αk F_αk -> quasi-energies λ_αk and eigenvector components
     137              :             ! F^{n,m}_αk = <n,m|αk> (band index n, sector m).
     138            2 :             CALL cp_cfm_set_all(cfm_eigenvectors, z_zero)
     139            2 :             CALL cp_cfm_heevd(floquet_matrix, cfm_eigenvectors, floquet_env%eigenvalues)
     140              : 
     141              :             ! Calculate sector weights,
     142              :             ! w_αk^(m) = Σ_{n=1}^{nao} |<n,m|αk>|^2 (with Σ_m w_αk^(m) = 1).
     143              :             ! Keep only the central weight w0_αk = w_αk^(0) and the outermost-rung weight
     144              :             ! wE_αk = w_αk^(-M) + w_αk^(+M)
     145            2 :             CALL floquet_sector_weights(floquet_env, cfm_eigenvectors)
     146              : 
     147              :             ! Truncation check: the m=0-dominated states (w0_αk > 1/2) must leak negligibly onto the
     148              :             ! outermost rungs, max_{α: w0_αk > 1/2} wE_αk ≤ EPS_FLOQUET, else M is too small.
     149            2 :             CALL check_floquet_convergence(bs_env, floquet_env)
     150              : 
     151              :             ! From λ_αk and w0_αk obtain, and store on the subgroup source for (ispin,ikp):
     152              :             !   DOS:
     153              :             !      A(k,E) = -(1/π) Im Tr_KS G^R_00(k,E) = -(1/π) Σ_α w0_αk Im[1/(E+iη-λ_αk)]
     154              :             !      with G^R_00 = [(E+iη) - H_F(k)]^{-1}_{m=0,m=0}, η = broadening/2.
     155              :             !      The central (m=0) block of the retarded Floquet Green's function gives the
     156              :             !      physical, time-averaged spectral function / density of states; see
     157              :             !      T. Oka and S. Kitamura, Annu. Rev. Condens. Matter Phys. 10, 387 (2019), and
     158              :             !      N. Tsuji, T. Oka, and H. Aoki, Phys. Rev. B 78, 235124 (2008).
     159              :             !   m=0 bands:
     160              :             !      ε^{0}_ik = λ_{α(i)}k, with band i placed at the eigenvalue where the cumulative
     161              :             !      m=0 weight Σ_{λ_αk<E} w0_αk first crosses i - 1/2.
     162              :             !      We know that: Σ_α w0_αk = nao. So we choose the matrix eigenvalue such that:
     163              :             !      N(ε) = Σ_{α : λ_αk ≤ ε} w0_αk first crosses i - 1/2 to find the m=0 band.
     164              :             !   Quasi-energy:
     165              :             !      ε̃_ik = ε^{0}_ik - mħΩ (choose m such that ε̃_ik is folded to (-ħΩ/2, ħΩ/2]).
     166            4 :             CALL calculate_floquet_observables(bs_env, para_env_sub, ispin, ikp, floquet_env)
     167              :          END DO
     168              : 
     169              :          ! Rough live K-Point progress
     170            4 :          IF (bs_env%unit_nr > 0) THEN
     171            1 :             n_source_done = n_source_done + 1
     172            1 :             n_est_done = MIN(n_source_done*ngroups, floquet_env%nkp_only_bs)
     173              :             WRITE (bs_env%unit_nr, '(T2,A,I6,A,F6.1,A)') &
     174            1 :                "FLOQUET PROGRESS | Estimated no. of k-points done: ", n_est_done, "  (", &
     175            2 :                100.0_dp*REAL(n_est_done, dp)/REAL(floquet_env%nkp_only_bs, dp), " %)"
     176              :          END IF
     177              :       END DO
     178              : 
     179              :       ! Write the m=0 bands ε^{0}_ik, the quasi-energies ε̃_ik and the DOS A(k,E) in their
     180              :       ! respective files
     181            2 :       CALL write_floquet_results(bs_env, floquet_env)
     182              : 
     183            2 :       DEALLOCATE (e_k, de_dk, dipole)
     184            2 :       DEALLOCATE (group_distribution)
     185            2 :       CALL floquet_env_release(floquet_env)
     186            2 :       CALL cp_cfm_release(floquet_matrix)
     187            2 :       CALL cp_cfm_release(cfm_eigenvectors)
     188            2 :       CALL cp_fm_struct_release(floquet_struct)
     189            2 :       CALL cp_blacs_env_release(blacs_env_sub)
     190            2 :       CALL mp_para_env_release(para_env_sub)
     191              : 
     192            2 :       CALL timestop(handle)
     193              : 
     194            4 :    END SUBROUTINE floquet
     195              : 
     196              : END MODULE floquet_main
        

Generated by: LCOV version 2.0-1