LCOV - code coverage report
Current view: top level - src/nonlinear_optics - nlo_bz_integration.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 92.9 % 226 210
Test Date: 2026-11-04 00:58:17 Functions: 100.0 % 8 8

            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              : !> \author Jan Wilhelm, with help from ChatGPT
       9              : 
      10              : MODULE nlo_integration
      11              :    USE cp_log_handling,                 ONLY: cp_logger_get_default_io_unit
      12              :    USE ieee_arithmetic,                 ONLY: ieee_is_finite
      13              :    USE kinds,                           ONLY: dp
      14              :    USE kubo_projected,                  ONLY: projected_eigensystem
      15              :    USE machine,                         ONLY: m_flush,&
      16              :                                               m_walltime
      17              :    USE mathconstants,                   ONLY: gaussi,&
      18              :                                               twopi
      19              :    USE nlo_current,                     ONLY: nlo_current_tensor_derivatives
      20              :    USE nlo_derivatives,                 ONLY: nlo_derivative_count,&
      21              :                                               nlo_derivative_indices,&
      22              :                                               nlo_equilibrium_density_derivatives,&
      23              :                                               nlo_n_derivatives,&
      24              :                                               nlo_orthonormal_operator_derivatives
      25              :    USE nlo_read_input,                  ONLY: nlo_frequency_case
      26              :    USE nlo_types,                       ONLY: nlo_type
      27              :    USE physcon,                         ONLY: evolt
      28              : #include "../base/base_uses.f90"
      29              : 
      30              :    IMPLICIT NONE
      31              : 
      32              :    PRIVATE
      33              : 
      34              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'nlo_integration'
      35              : 
      36              :    PUBLIC :: nlo_bz_integration
      37              : 
      38              : CONTAINS
      39              : 
      40              : ! **************************************************************************************************
      41              : !> \brief Integrate the density-response current over the Brillouin zone.
      42              : !> \param nlo_env NLO settings and data for the calculation
      43              : ! **************************************************************************************************
      44           22 :    SUBROUTINE nlo_bz_integration(nlo_env)
      45              :       TYPE(nlo_type), INTENT(INOUT)                      :: nlo_env
      46              : 
      47              :       CHARACTER(len=*), PARAMETER :: routineN = 'nlo_bz_integration'
      48              : 
      49           22 :       COMPLEX(dp), ALLOCATABLE                           :: curvature(:, :, :), matrix_term(:, :)
      50              :       INTEGER                                            :: handle, i_case, ia, ic, ik, iw, nocc, &
      51              :                                                             pair, spin
      52              :       LOGICAL                                            :: zero_output
      53              :       REAL(KIND=dp)                                      :: energy_ev(5), k(3), local_projection, &
      54              :                                                             occ_factor, omega(5)
      55              : 
      56           22 :       CALL timeset(routineN, handle)
      57              : 
      58           22 :       CALL allocate_nlo_quantities(nlo_env)
      59              :       ALLOCATE (curvature(nlo_env%n_bands, nlo_env%n_bands, 3), &
      60          176 :                 matrix_term(nlo_env%n_bands, nlo_env%n_bands))
      61              : 
      62           22 :       nlo_env%projection_error = 0.0_dp
      63           22 :       nlo_env%min_direct_gap = HUGE(1.0_dp)
      64           22 :       nlo_env%progress_total = 1 + (nlo_env%n_kpoints - 1)/nlo_env%para_env%num_pe
      65           22 :       IF (nlo_env%progress_total > HUGE(nlo_env%progress_total)/nlo_env%n_spin) THEN
      66            0 :          CPABORT('NLO work count exceeds integer indexing.')
      67              :       END IF
      68           22 :       nlo_env%progress_total = nlo_env%n_spin*nlo_env%progress_total
      69           22 :       nlo_env%progress_done = 0
      70           22 :       nlo_env%progress_next = 1
      71           22 :       nlo_env%progress_start = m_walltime()
      72              : 
      73           44 :       DO spin = 1, nlo_env%n_spin
      74           22 :          IF (nlo_env%mos(spin)%maxocc <= 0.0_dp) THEN
      75            0 :             CPABORT('NONLINEAR_OPTICS requires positive band occupation.')
      76              :          END IF
      77           22 :          occ_factor = nlo_env%mos(spin)%maxocc
      78           22 :          nocc = NINT(REAL(nlo_env%mos(spin)%nelectron, KIND=dp)/occ_factor)
      79           22 :          IF (nocc < 1 .OR. nocc >= nlo_env%n_bands .OR. &
      80              :              ABS(nocc*occ_factor - nlo_env%mos(spin)%nelectron) > 1.e-8_dp) THEN
      81            0 :             CPABORT('NLO needs integer occupied-band count and empty bands.')
      82              :          END IF
      83          498 :          nlo_env%occupied = 0
      84          108 :          nlo_env%occupied(1:nocc) = 1
      85           68 :          DO ik = 1, nlo_env%n_kpoints
      86           24 :             IF (MOD(ik - 1, nlo_env%para_env%num_pe) /= nlo_env%para_env%mepos) CYCLE
      87              :             k = REAL([MOD(ik - 1, nlo_env%k_mesh(1)), &
      88              :                       MOD((ik - 1)/nlo_env%k_mesh(1), nlo_env%k_mesh(2)), &
      89              :                       (ik - 1)/(nlo_env%k_mesh(1)*nlo_env%k_mesh(2))], KIND=dp)/ &
      90           48 :                 REAL(nlo_env%k_mesh, KIND=dp)
      91              :             ! Build H(k), S(k), d(k) and their analytic Cartesian k derivatives.
      92           12 :             CALL ao_derivatives_at_k(nlo_env, k, spin)
      93           12 :             CALL hermitize_derivatives(nlo_env%hamiltonian_derivatives, local_projection)
      94              : 
      95           12 :             nlo_env%projection_error = MAX(nlo_env%projection_error, local_projection)
      96           12 :             IF (local_projection > 1.e-5_dp) THEN
      97            0 :                CPABORT('NLO Hamiltonian images are not Hermitian.')
      98              :             END IF
      99              :             ! X = S^(-1/2) orthonormalizes the AO basis; U diagonalizes X H X here.
     100              :             ! Express all operator derivatives in this same eigenvector basis U.
     101              :             CALL nlo_ao_to_band_derivatives( &
     102              :                nlo_env%overlap_derivatives, nlo_env%hamiltonian_derivatives, &
     103              :                nlo_env%position_derivatives, &
     104              :                nlo_env%occupied, nlo_env%energies, nlo_env%band_hamiltonian_derivatives, &
     105              :                nlo_env%band_connection_derivatives, nlo_env%rho0_derivatives, &
     106              :                nlo_env%velocity, &
     107           12 :                active_grade=nlo_env%order)
     108              : 
     109              :             nlo_env%min_direct_gap = MIN( &
     110              :                                      nlo_env%min_direct_gap, &
     111           12 :                                      nlo_env%energies(nocc + 1) - nlo_env%energies(nocc))
     112              :             ! F_ca = d_c A_a - d_a A_c - i[A_c,A_a], reused for every frequency.
     113           36 :             DO ic = 1, 2
     114           72 :                DO ia = ic + 1, 3
     115           36 :                   pair = (ic - 1)*(6 - ic)/2 + ia - ic
     116              :                   curvature(:, :, pair) = &
     117              :                      nlo_env%band_connection_derivatives(:, :, ia, ic + 1) - &
     118        18204 :                      nlo_env%band_connection_derivatives(:, :, ic, ia + 1)
     119           36 :                   matrix_term(:, :) = MATMUL(nlo_env%band_connection_derivatives(:, :, ic, 1), &
     120       885852 :                                              nlo_env%band_connection_derivatives(:, :, ia, 1))
     121        18204 :                   curvature(:, :, pair) = curvature(:, :, pair) - gaussi*matrix_term
     122           36 :                   matrix_term(:, :) = MATMUL(nlo_env%band_connection_derivatives(:, :, ia, 1), &
     123       885852 :                                              nlo_env%band_connection_derivatives(:, :, ic, 1))
     124        18228 :                   curvature(:, :, pair) = curvature(:, :, pair) + gaussi*matrix_term
     125              :                END DO
     126              :             END DO
     127           25 :             DO i_case = 1, nlo_env%n_frequency_combinations
     128              :                CALL nlo_frequency_case(nlo_env%frequency, nlo_env%order, i_case, &
     129           13 :                                        energy_ev, zero_output)
     130           13 :                IF (zero_output) THEN
     131            0 :                   CPABORT('Invalid NONLINEAR_OPTICS frequency case.')
     132              :                END IF
     133           13 :                omega = 0.0_dp
     134           53 :                omega(1:nlo_env%order) = energy_ev(1:nlo_env%order)/evolt
     135              :                ! Compute J_i^(n)(k) from rho^(n): -Tr[v_i rho^(n)] plus curvature current.
     136              :                ! Each rho^(n) follows from lower orders at Omega = sum_j omega_j.
     137              :                CALL nlo_current_tensor_derivatives( &
     138              :                   nlo_env%order, omega(1:nlo_env%order), nlo_env%energies, &
     139              :                   1.0_dp/nlo_env%t1_au, 1.0_dp/nlo_env%t2_au, &
     140              :                   nlo_env%transition_width_au, nlo_env%band_hamiltonian_derivatives, &
     141              :                   nlo_env%band_connection_derivatives, curvature, &
     142              :                   nlo_env%rho0_derivatives, &
     143              :                   nlo_env%velocity, &
     144           13 :                   nlo_env%current_flat, elements=nlo_env%elements)
     145              : 
     146              :                ! Sum J_i^(n)(k) over spin and k; f_spin is the band occupation factor.
     147              :                ! I_i^(n) = sum_(spin,k) f_spin J_i^(n)(k) / N_k.
     148              :                nlo_env%current_spectrum(:, :, i_case) = &
     149              :                   nlo_env%current_spectrum(:, :, i_case) + &
     150         3805 :                   occ_factor*nlo_env%current_flat/REAL(nlo_env%n_kpoints, KIND=dp)
     151              :             END DO
     152           46 :             IF (nlo_env%para_env%mepos == 0) CALL report_nlo_progress(nlo_env)
     153              : 
     154              :          END DO
     155              :       END DO
     156           22 :       CALL nlo_env%para_env%max(nlo_env%projection_error)
     157           22 :       CALL nlo_env%para_env%min(nlo_env%min_direct_gap)
     158           22 :       iw = cp_logger_get_default_io_unit()
     159           22 :       IF (iw > 0) THEN
     160              :          WRITE (iw, '(T2,A,T69,F9.2,1X,A)') &
     161           11 :             'NONLINEAR_OPTICS| Minimum occupied-empty gap:', &
     162           22 :             nlo_env%min_direct_gap*evolt, 'eV'
     163              :       END IF
     164           22 :       CALL release_nlo_quantities(nlo_env)
     165              : 
     166           22 :       CALL timestop(handle)
     167              : 
     168           44 :    END SUBROUTINE nlo_bz_integration
     169              : 
     170              : ! **************************************************************************************************
     171              : !> \brief Project all analytic Hamiltonian derivatives onto the Hermitian image subspace.
     172              : !> \param derivatives AO Hamiltonian derivatives over lattice images
     173              : !> \param relative_error Largest relative change from enforcing Hermiticity
     174              : !> \note This linear projection commutes with all k derivatives.
     175              : ! **************************************************************************************************
     176           12 :    SUBROUTINE hermitize_derivatives(derivatives, relative_error)
     177              :       COMPLEX(dp), INTENT(INOUT)                         :: derivatives(:, :, :)
     178              :       REAL(KIND=dp), INTENT(OUT)                         :: relative_error
     179              : 
     180           24 :       COMPLEX(dp) :: adjoint(SIZE(derivatives, 1), SIZE(derivatives, 2))
     181              :       INTEGER                                            :: ij
     182              :       REAL(KIND=dp)                                      :: scale
     183              : 
     184           12 :       relative_error = 0.0_dp
     185          325 :       DO ij = 1, SIZE(derivatives, 3)
     186       157287 :          adjoint = CONJG(TRANSPOSE(derivatives(:, :, ij)))
     187       157287 :          scale = MAX(1.0_dp, MAXVAL(ABS(derivatives(:, :, ij))))
     188       157287 :          relative_error = MAX(relative_error, MAXVAL(ABS(derivatives(:, :, ij) - adjoint))/scale)
     189       157299 :          derivatives(:, :, ij) = 0.5_dp*(derivatives(:, :, ij) + adjoint)
     190              :       END DO
     191              : 
     192           12 :    END SUBROUTINE hermitize_derivatives
     193              : 
     194              : ! **************************************************************************************************
     195              : !> \brief Fourier-transform AO H, S, and position matrices at one optical k point.
     196              : !> \param nlo_env NLO settings and data for the calculation
     197              : !> \param k Optical k point in fractional coordinates
     198              : !> \param spin Spin channel of the converged SCF solution
     199              : !> \note For X = H, S, or the AO position matrix d_a, and Cartesian k:
     200              : !>       dX(k)/dk_x = sum_R i R_x exp(i k·R) X(R).
     201              : !>       R is a real-space lattice vector. The input k uses fractional coordinates:
     202              : !>       k·R = 2 pi k_frac·R_int. Higher derivatives add factors i R_a;
     203              : !>       each stored Taylor coefficient is divided by its factorial.
     204              : ! **************************************************************************************************
     205           12 :    SUBROUTINE ao_derivatives_at_k(nlo_env, k, spin)
     206              :       TYPE(nlo_type), INTENT(INOUT)                      :: nlo_env
     207              :       REAL(KIND=dp), INTENT(IN)                          :: k(3)
     208              :       INTEGER, INTENT(IN)                                :: spin
     209              : 
     210              :       CHARACTER(len=*), PARAMETER :: routineN = 'ao_derivatives_at_k'
     211              : 
     212              :       INTEGER                                            :: alpha(3, nlo_n_derivatives), grade, &
     213              :                                                             handle, ia, ij, j, n, na, nh, ni
     214              :       REAL(KIND=dp)                                      :: divisor
     215              : 
     216           12 :       CALL timeset(routineN, handle)
     217              : 
     218              :       ASSOCIATE (h_image => nlo_env%h_ao_R(spin, :, :, :), &
     219              :                  s_image => nlo_env%s_ao_R(1, :, :, :), &
     220              :                  moment_image => nlo_env%d_ao_R, &
     221              :                  image_cells => nlo_env%images%index_to_cell, &
     222              :                  hmat => nlo_env%cell%hmat, &
     223              :                  h_derivatives => nlo_env%hamiltonian_derivatives, &
     224              :                  s_derivatives => nlo_env%overlap_derivatives, &
     225              :                  moment_derivatives => nlo_env%position_derivatives)
     226           12 :          h_derivatives = 0.0_dp
     227           12 :          s_derivatives = 0.0_dp
     228           12 :          moment_derivatives = 0.0_dp
     229           12 :          n = SIZE(h_image, 1)
     230           12 :          ni = SIZE(s_image, 3)
     231            0 :          CPASSERT(n >= 2)
     232           12 :          CPASSERT(SIZE(h_image, 2) == n)
     233           12 :          CPASSERT(SIZE(h_image, 3) >= 1)
     234           12 :          CPASSERT(SIZE(s_image, 1) == n)
     235           12 :          CPASSERT(SIZE(s_image, 2) == n)
     236           12 :          CPASSERT(ni >= 1)
     237           60 :          CPASSERT(ALL(SHAPE(moment_image) == [3, n, n, ni]))
     238           12 :          CPASSERT(SIZE(h_image, 3) == ni)
     239           36 :          CPASSERT(ALL(SHAPE(image_cells) == [3, ni]))
     240           12 :          nh = nlo_derivative_count(nlo_env%order)
     241           12 :          na = nlo_derivative_count(nlo_env%order - 1)
     242           48 :          CPASSERT(ALL(SHAPE(h_derivatives) == [n, n, nh]))
     243           48 :          CPASSERT(ALL(SHAPE(s_derivatives) == [n, n, nh]))
     244           60 :          CPASSERT(ALL(SHAPE(moment_derivatives) == [n, n, 3, na]))
     245              :          IF (.NOT. ALL(ieee_is_finite(h_image)) .OR. .NOT. ALL(ieee_is_finite(s_image)) .OR. &
     246              :              .NOT. ALL(ieee_is_finite(moment_image)) .OR. &
     247       677160 :              .NOT. ALL(ieee_is_finite(k)) .OR. .NOT. ALL(ieee_is_finite(hmat))) THEN
     248            0 :             CPABORT('NONLINEAR_OPTICS numerical derivative construction failed.')
     249              :          END IF
     250           12 :          grade = nlo_env%order
     251           12 :          CPASSERT(grade >= 2 .AND. grade <= 5)
     252           12 :          CALL nlo_derivative_indices(alpha)
     253          325 :          DO ij = 1, nlo_derivative_count(grade)
     254              :             divisor = 1.0_dp
     255         1252 :             DO ia = 1, 3
     256         1672 :                DO j = 2, alpha(ia, ij)
     257         1359 :                   divisor = divisor*REAL(j, KIND=dp)
     258              :                END DO
     259              :             END DO
     260              :             CALL rs_to_kp_multi_deriv(h_image, h_derivatives(:, :, ij), image_cells, k, &
     261          313 :                                       alpha(:, ij), hmat)
     262              :             CALL rs_to_kp_multi_deriv(s_image, s_derivatives(:, :, ij), image_cells, k, &
     263          313 :                                       alpha(:, ij), hmat)
     264       157287 :             h_derivatives(:, :, ij) = h_derivatives(:, :, ij)/divisor
     265       157287 :             s_derivatives(:, :, ij) = s_derivatives(:, :, ij)/divisor
     266         1252 :             IF (SUM(alpha(:, ij)) >= grade) CYCLE
     267          712 :             DO ia = 1, 3
     268              :                CALL rs_to_kp_multi_deriv(moment_image(ia, :, :, :), &
     269              :                                          moment_derivatives(:, :, ia, ij), image_cells, k, &
     270          525 :                                          alpha(:, ij), hmat)
     271       264604 :                moment_derivatives(:, :, ia, ij) = moment_derivatives(:, :, ia, ij)/divisor
     272              :             END DO
     273              :          END DO
     274              :          IF (.NOT. ALL(ieee_is_finite(REAL(h_derivatives, KIND=dp))) &
     275              :              .OR. .NOT. ALL(ieee_is_finite(AIMAG(h_derivatives))) .OR. &
     276              :              .NOT. ALL(ieee_is_finite(REAL(s_derivatives, KIND=dp))) &
     277              :              .OR. .NOT. ALL(ieee_is_finite(AIMAG(s_derivatives))) .OR. &
     278      1158152 :              .NOT. ALL(ieee_is_finite(REAL(moment_derivatives, KIND=dp))) .OR. &
     279           12 :              .NOT. ALL(ieee_is_finite(AIMAG(moment_derivatives)))) THEN
     280            0 :             CPABORT('NONLINEAR_OPTICS numerical derivative construction failed.')
     281              :          END IF
     282              : 
     283              :       END ASSOCIATE
     284              : 
     285           12 :       CALL timestop(handle)
     286              : 
     287           12 :    END SUBROUTINE ao_derivatives_at_k
     288              : 
     289              : ! **************************************************************************************************
     290              : !> \brief Convert AO operators to band matrices and build the equilibrium density.
     291              : !> \param overlap AO overlap and its k derivatives
     292              : !> \param hamiltonian AO Hamiltonian and its k derivatives
     293              : !> \param moment AO position-matrix derivatives through one grade below the response order
     294              : !> \param occupied One for an occupied band, zero for an empty band
     295              : !> \param energies Band energies in Hartree at this k point
     296              : !> \param band_h Hamiltonian derivatives in the eigenvector basis
     297              : !> \param band_a Position connection derivatives in the eigenvector basis
     298              : !> \param rho0 Equilibrium density and its k derivatives for unit occupation
     299              : !> \param velocity Band velocity at this k point, in bohr per atomic time
     300              : !> \param active_grade Highest derivative grade required for the response
     301              : !> \note First X=S^(-1/2) makes the AO basis orthonormal. U diagonalizes
     302              : !>       X H X at this k point. The same U is applied to every k derivative
     303              : !>       of H and the position matrix, so no derivative of U is needed.
     304              : !>       Occupied bands then define rho0; field-driven densities are built later.
     305              : ! **************************************************************************************************
     306           12 :    SUBROUTINE nlo_ao_to_band_derivatives(overlap, hamiltonian, moment, occupied, &
     307           12 :                                          energies, band_h, band_a, rho0, velocity, &
     308              :                                          active_grade)
     309              :       COMPLEX(dp), DIMENSION(:, :, :), INTENT(IN)        :: overlap, hamiltonian
     310              :       COMPLEX(dp), INTENT(IN)                            :: moment(:, :, :, :)
     311              :       INTEGER, INTENT(IN)                                :: occupied(:)
     312              :       REAL(KIND=dp), INTENT(OUT)                         :: energies(:)
     313              :       COMPLEX(dp), INTENT(OUT)                           :: band_h(:, :, :), band_a(:, :, :, :), &
     314              :                                                             rho0(:, :, :), velocity(:, :, :)
     315              :       INTEGER, INTENT(IN), OPTIONAL                      :: active_grade
     316              : 
     317              :       CHARACTER(len=*), PARAMETER :: routineN = 'nlo_ao_to_band_derivatives'
     318              : 
     319           12 :       COMPLEX(dp), ALLOCATABLE                           :: identity(:, :), matrix_term(:, :), &
     320           12 :                                                             orth_h(:, :, :), u(:, :)
     321           12 :       COMPLEX(dp), ALLOCATABLE, DIMENSION(:, :, :, :)    :: connection
     322              :       INTEGER                                            :: alpha(3, nlo_n_derivatives), grade, &
     323              :                                                             handle, ia, ideriv, ij, info, n, na, nh
     324              : 
     325           12 :       CALL timeset(routineN, handle)
     326              : 
     327          260 :       energies = 0.0_dp
     328       157299 :       band_h = 0.0_dp
     329       264478 :       band_a = 0.0_dp
     330       157299 :       rho0 = 0.0_dp
     331        18216 :       velocity = 0.0_dp
     332           12 :       n = SIZE(energies)
     333           12 :       CPASSERT(.NOT. (n < 2 .OR. SIZE(occupied) /= n))
     334           12 :       grade = 5
     335           12 :       IF (PRESENT(active_grade)) grade = active_grade
     336           12 :       CPASSERT(grade >= 2 .AND. grade <= 5)
     337           12 :       nh = nlo_derivative_count(grade)
     338           12 :       na = nlo_derivative_count(grade - 1)
     339           48 :       CPASSERT(ALL(SHAPE(overlap) == [n, n, nh]))
     340           48 :       CPASSERT(ALL(SHAPE(hamiltonian) == [n, n, nh]))
     341           60 :       CPASSERT(ALL(SHAPE(moment) == [n, n, 3, na]))
     342           48 :       CPASSERT(ALL(SHAPE(band_h) == [n, n, nh]))
     343           60 :       CPASSERT(ALL(SHAPE(band_a) == [n, n, 3, na]))
     344           48 :       CPASSERT(ALL(SHAPE(rho0) == [n, n, nh]))
     345           48 :       CPASSERT(ALL(SHAPE(velocity) == [n, n, 3]))
     346              :       ALLOCATE (connection(n, n, 3, na), identity(n, n), matrix_term(n, n), &
     347          204 :                 orth_h(n, n, nh), u(n, n))
     348              : 
     349              :       CALL nlo_orthonormal_operator_derivatives(overlap, hamiltonian, moment, orth_h, &
     350           12 :                                                 connection, active_grade=grade)
     351              : 
     352           12 :       identity = 0.0_dp
     353          260 :       DO ij = 1, n
     354          260 :          identity(ij, ij) = 1.0_dp
     355              :       END DO
     356              : 
     357           12 :       CALL projected_eigensystem(orth_h(:, :, 1), identity, energies, u, info)
     358           12 :       IF (info /= 0) CPABORT('NONLINEAR_OPTICS band eigensolver failed.')
     359              : 
     360          325 :       DO ij = 1, nh
     361     15134215 :          matrix_term(:, :) = MATMUL(orth_h(:, :, ij), u)
     362      7802725 :          band_h(:, :, ij) = MATMUL(CONJG(TRANSPOSE(u)), matrix_term)
     363          313 :          IF (ij > na) CYCLE
     364          712 :          DO ia = 1, 3
     365     25438083 :             matrix_term(:, :) = MATMUL(connection(:, :, ia, ij), u)
     366     13115266 :             band_a(:, :, ia, ij) = MATMUL(CONJG(TRANSPOSE(u)), matrix_term)
     367              :          END DO
     368              :       END DO
     369              : 
     370              :       CALL nlo_equilibrium_density_derivatives(energies, occupied, band_h, rho0, &
     371           12 :                                                active_grade=grade)
     372              : 
     373           12 :       CALL nlo_derivative_indices(alpha)
     374           48 :       DO ia = 1, 3
     375              :          ideriv = 0
     376          975 :          DO ij = 1, nh
     377         3792 :             IF (alpha(ia, ij) == 1 .AND. SUM(alpha(:, ij)) == 1) ideriv = ij
     378              :          END DO
     379           36 :          CPASSERT(ideriv /= 0)
     380       452028 :          matrix_term(:, :) = MATMUL(band_a(:, :, ia, 1), band_h(:, :, 1))
     381        18204 :          velocity(:, :, ia) = band_h(:, :, ideriv) - gaussi*matrix_term
     382       452028 :          matrix_term(:, :) = MATMUL(band_h(:, :, 1), band_a(:, :, ia, 1))
     383        18216 :          velocity(:, :, ia) = velocity(:, :, ia) + gaussi*matrix_term
     384              :       END DO
     385        36432 :       IF (.NOT. ALL(ieee_is_finite(REAL(velocity, KIND=dp))) .OR. &
     386              :           .NOT. ALL(ieee_is_finite(AIMAG(velocity)))) THEN
     387            0 :          CPABORT('NONLINEAR_OPTICS numerical derivative construction failed.')
     388              :       END IF
     389              : 
     390           12 :       CALL timestop(handle)
     391              : 
     392           24 :    END SUBROUTINE nlo_ao_to_band_derivatives
     393              : 
     394              : ! **************************************************************************************************
     395              : !> \brief Analytic Cartesian mixed k derivative of a real-space AO matrix.
     396              : !> \param rs_real Real-space matrix for every lattice image
     397              : !> \param ks_complex Requested derivative in k space
     398              : !> \param index_to_cell Integer lattice vector for each image
     399              : !> \param xkp k point in reciprocal fractional coordinates
     400              : !> \param deriv_orders Number of derivatives in Cartesian x, y, z
     401              : !> \param hmat Direct-cell vectors as columns, in the same length units as k^-1
     402              : !> \note The Fourier factor is exp(2πi k_frac dot R_int). Each Cartesian derivative
     403              : !>       contributes i R_cart. This avoids phase-sensitive finite differences.
     404              : ! **************************************************************************************************
     405         1151 :    SUBROUTINE rs_to_kp_multi_deriv(rs_real, ks_complex, index_to_cell, xkp, deriv_orders, hmat)
     406              :       REAL(KIND=dp), INTENT(IN)                          :: rs_real(:, :, :)
     407              :       COMPLEX(dp), INTENT(OUT)                           :: ks_complex(:, :)
     408              :       INTEGER, INTENT(IN)                                :: index_to_cell(:, :)
     409              :       REAL(KIND=dp), INTENT(IN)                          :: xkp(3)
     410              :       INTEGER, INTENT(IN)                                :: deriv_orders(3)
     411              :       REAL(KIND=dp), INTENT(IN)                          :: hmat(3, 3)
     412              : 
     413              :       COMPLEX(dp)                                        :: factor
     414              :       INTEGER                                            :: i_axis, i_image, n_images
     415              :       REAL(KIND=dp)                                      :: cell_index(3), cell_vector(3), phase
     416              : 
     417         4604 :       IF (ANY(deriv_orders < 0)) CPABORT('Negative k derivative order requested.')
     418         1151 :       n_images = SIZE(rs_real, 3)
     419         1151 :       IF (n_images < 1) CPABORT('No real-space lattice images supplied.')
     420         1151 :       IF (SIZE(index_to_cell, 1) /= 3 .OR. SIZE(index_to_cell, 2) /= n_images) THEN
     421            0 :          CPABORT('Real-space matrices and lattice-image indices are inconsistent.')
     422              :       END IF
     423         1151 :       IF (SIZE(ks_complex, 1) /= SIZE(rs_real, 1) .OR. SIZE(ks_complex, 2) /= SIZE(rs_real, 2)) THEN
     424            0 :          CPABORT('Real-space and k-space matrix shapes are inconsistent.')
     425              :       END IF
     426       578865 :       ks_complex = CMPLX(0.0_dp, 0.0_dp, kind=dp)
     427        23020 :       DO i_image = 1, n_images
     428        87476 :          cell_index = REAL(index_to_cell(:, i_image), KIND=dp)
     429       284297 :          cell_vector = MATMUL(hmat, cell_index)
     430        87476 :          phase = twopi*SUM(xkp*cell_index)
     431        21869 :          factor = CMPLX(COS(phase), SIN(phase), kind=dp)
     432        87476 :          DO i_axis = 1, 3
     433        87476 :             factor = factor*CMPLX(0.0_dp, cell_vector(i_axis), kind=dp)**deriv_orders(i_axis)
     434              :          END DO
     435     10999586 :          ks_complex = ks_complex + factor*rs_real(:, :, i_image)
     436              :       END DO
     437              : 
     438         1151 :    END SUBROUTINE rs_to_kp_multi_deriv
     439              : 
     440              : ! **************************************************************************************************
     441              : !> \brief Allocate the per-k matrices and rank-local frequency accumulators once.
     442              : !> \param nlo_env NLO settings and data for the calculation
     443              : ! **************************************************************************************************
     444           22 :    SUBROUTINE allocate_nlo_quantities(nlo_env)
     445              :       TYPE(nlo_type), INTENT(INOUT)                      :: nlo_env
     446              : 
     447              :       INTEGER                                            :: na, nh
     448              : 
     449           22 :       nh = nlo_derivative_count(nlo_env%order)
     450           22 :       na = nlo_derivative_count(nlo_env%order - 1)
     451           66 :       ALLOCATE (nlo_env%energies(nlo_env%n_bands))
     452           66 :       ALLOCATE (nlo_env%occupied(nlo_env%n_bands))
     453          110 :       ALLOCATE (nlo_env%hamiltonian_derivatives(nlo_env%n_bands, nlo_env%n_bands, nh))
     454           88 :       ALLOCATE (nlo_env%overlap_derivatives(nlo_env%n_bands, nlo_env%n_bands, nh))
     455          132 :       ALLOCATE (nlo_env%position_derivatives(nlo_env%n_bands, nlo_env%n_bands, 3, na))
     456           88 :       ALLOCATE (nlo_env%band_hamiltonian_derivatives(nlo_env%n_bands, nlo_env%n_bands, nh))
     457           88 :       ALLOCATE (nlo_env%band_connection_derivatives(nlo_env%n_bands, nlo_env%n_bands, 3, na))
     458           88 :       ALLOCATE (nlo_env%rho0_derivatives(nlo_env%n_bands, nlo_env%n_bands, nh))
     459           88 :       ALLOCATE (nlo_env%velocity(nlo_env%n_bands, nlo_env%n_bands, 3))
     460           66 :       ALLOCATE (nlo_env%current_flat(3, 3**nlo_env%order))
     461            0 :       ALLOCATE (nlo_env%current_spectrum(3, 3**nlo_env%order, nlo_env%n_frequency_combinations), &
     462         7456 :                 SOURCE=(0.0_dp, 0.0_dp))
     463              : 
     464           22 :    END SUBROUTINE allocate_nlo_quantities
     465              : 
     466              : ! **************************************************************************************************
     467              : !> \brief Print completed spin/k tasks when the next percentage threshold is crossed.
     468              : !> \param nlo_env NLO settings and data for the calculation
     469              : ! **************************************************************************************************
     470           11 :    SUBROUTINE report_nlo_progress(nlo_env)
     471              :       TYPE(nlo_type), INTENT(INOUT)                      :: nlo_env
     472              : 
     473              :       REAL(KIND=dp), PARAMETER :: &
     474              :          marks(9) = [0.1_dp, 0.3_dp, 1.0_dp, 3.0_dp, 10.0_dp, 30.0_dp, 60.0_dp, 90.0_dp, 100.0_dp]
     475              : 
     476              :       INTEGER                                            :: iw
     477              :       REAL(KIND=dp)                                      :: percent
     478              : 
     479           11 :       nlo_env%progress_done = nlo_env%progress_done + 1
     480           11 :       percent = 100.0_dp*REAL(nlo_env%progress_done, KIND=dp)/REAL(nlo_env%progress_total, KIND=dp)
     481           11 :       CPASSERT(.NOT. (nlo_env%progress_next > SIZE(marks)))
     482           11 :       IF (percent < marks(nlo_env%progress_next)) THEN
     483            0 :          RETURN
     484              :       END IF
     485           11 :       iw = cp_logger_get_default_io_unit()
     486           11 :       IF (iw > 0) THEN
     487           11 :          IF (nlo_env%progress_total < 10000) THEN
     488              :             WRITE (iw, '(T2,A,I0,A,I0,7X,F5.1,A,4X,A,T69,F10.2,1X,A)') &
     489           11 :                'NONLINEAR_OPTICS| Progress:  ', nlo_env%progress_done, ' / ', &
     490           11 :                nlo_env%progress_total, percent, '%', 'Total time:', &
     491           22 :                m_walltime() - nlo_env%progress_start, 's'
     492              :          ELSE
     493              :             WRITE (iw, '(T2,A,I0,A,I0,7X,F5.1,A)') &
     494            0 :                'NONLINEAR_OPTICS| Progress:  ', nlo_env%progress_done, ' / ', &
     495            0 :                nlo_env%progress_total, percent, '%'
     496              :             WRITE (iw, '(T2,A,T69,F10.2,1X,A)') &
     497            0 :                'NONLINEAR_OPTICS| Total time:', m_walltime() - nlo_env%progress_start, 's'
     498              :          END IF
     499           11 :          CALL m_flush(iw)
     500              :       END IF
     501          110 :       DO WHILE (nlo_env%progress_next <= SIZE(marks))
     502           99 :          IF (percent < marks(nlo_env%progress_next)) EXIT
     503           99 :          nlo_env%progress_next = nlo_env%progress_next + 1
     504              :       END DO
     505              : 
     506              :    END SUBROUTINE report_nlo_progress
     507              : 
     508              : ! **************************************************************************************************
     509              : !> \brief Free the per-k workspace after the MPI reduction and validation.
     510              : !> \param nlo_env NLO settings and data for the calculation
     511              : ! **************************************************************************************************
     512           22 :    SUBROUTINE release_nlo_quantities(nlo_env)
     513              :       TYPE(nlo_type), INTENT(INOUT)                      :: nlo_env
     514              : 
     515           22 :       DEALLOCATE (nlo_env%energies, nlo_env%occupied)
     516           22 :       DEALLOCATE (nlo_env%hamiltonian_derivatives, nlo_env%overlap_derivatives)
     517           22 :       DEALLOCATE (nlo_env%position_derivatives)
     518           22 :       DEALLOCATE (nlo_env%band_hamiltonian_derivatives, nlo_env%band_connection_derivatives)
     519           22 :       DEALLOCATE (nlo_env%rho0_derivatives, nlo_env%velocity, nlo_env%current_flat)
     520              : 
     521           22 :    END SUBROUTINE release_nlo_quantities
     522              : 
     523              : END MODULE nlo_integration
        

Generated by: LCOV version 2.0-1