LCOV - code coverage report
Current view: top level - src - qs_density_fit.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 93.8 % 341 320
Test Date: 2026-09-03 07:32:15 Functions: 100.0 % 14 14

            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 Constrained fitting of a real-space density by an AO density matrix.
      10              : !>        This is a dense reference implementation with cubic spectral projections.
      11              : !> \author CP2K developers group
      12              : ! **************************************************************************************************
      13              : MODULE qs_density_fit
      14              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_type
      15              :    USE cp_control_types,                ONLY: dft_control_type
      16              :    USE cp_dbcsr_api,                    ONLY: dbcsr_copy,&
      17              :                                               dbcsr_create,&
      18              :                                               dbcsr_get_info,&
      19              :                                               dbcsr_p_type,&
      20              :                                               dbcsr_release,&
      21              :                                               dbcsr_set,&
      22              :                                               dbcsr_type,&
      23              :                                               dbcsr_type_symmetric
      24              :    USE cp_dbcsr_contrib,                ONLY: dbcsr_dot
      25              :    USE cp_dbcsr_operations,             ONLY: copy_dbcsr_to_fm,&
      26              :                                               copy_fm_to_dbcsr
      27              :    USE cp_fm_basic_linalg,              ONLY: cp_fm_column_scale,&
      28              :                                               cp_fm_scale_and_add,&
      29              :                                               cp_fm_trace,&
      30              :                                               cp_fm_uplo_to_full
      31              :    USE cp_fm_cholesky,                  ONLY: cp_fm_cholesky_decompose,&
      32              :                                               cp_fm_cholesky_restore
      33              :    USE cp_fm_diag,                      ONLY: cp_fm_syevd
      34              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      35              :                                               cp_fm_struct_release,&
      36              :                                               cp_fm_struct_type
      37              :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      38              :                                               cp_fm_release,&
      39              :                                               cp_fm_to_fm,&
      40              :                                               cp_fm_type
      41              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      42              :                                               cp_logger_get_default_unit_nr,&
      43              :                                               cp_logger_type
      44              :    USE kinds,                           ONLY: dp
      45              :    USE message_passing,                 ONLY: mp_para_env_type
      46              :    USE parallel_gemm_api,               ONLY: parallel_gemm
      47              :    USE pw_env_types,                    ONLY: pw_env_get,&
      48              :                                               pw_env_type
      49              :    USE pw_methods,                      ONLY: pw_axpy,&
      50              :                                               pw_copy,&
      51              :                                               pw_integral_ab,&
      52              :                                               pw_integrate_function
      53              :    USE pw_pool_types,                   ONLY: pw_pool_type
      54              :    USE pw_types,                        ONLY: pw_c1d_gs_type,&
      55              :                                               pw_r3d_rs_type
      56              :    USE qs_collocate_density,            ONLY: calculate_rho_elec
      57              :    USE qs_environment_types,            ONLY: get_qs_env,&
      58              :                                               qs_environment_type
      59              :    USE qs_integrate_potential,          ONLY: integrate_v_rspace
      60              :    USE qs_ks_types,                     ONLY: qs_ks_env_type
      61              :    USE qs_rho_types,                    ONLY: qs_rho_get,&
      62              :                                               qs_rho_type
      63              : #include "./base/base_uses.f90"
      64              : 
      65              :    IMPLICIT NONE
      66              :    PRIVATE
      67              : 
      68              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_density_fit'
      69              : 
      70              :    PUBLIC :: fit_constrained_density, fit_relative_entropy_density
      71              : 
      72              : CONTAINS
      73              : 
      74              : ! **************************************************************************************************
      75              : !> \brief Fits the density currently stored in rho_struct with a spin-restricted AO density matrix.
      76              : !>        The density matrix is projected in an orthonormal AO representation onto
      77              : !>        0 <= P <= 2 and Tr(P) = Ne after every gradient step.
      78              : !> \param qs_env QS environment
      79              : !> \param rho_struct density structure containing the target density on entry and the fit on exit
      80              : !> \param max_iter maximum number of projected-gradient iterations
      81              : !> \param eps_rms requested RMS density residual
      82              : !> \param step_size initial spectral projected-gradient step
      83              : !> \param max_backtrack maximum number of step halvings per iteration
      84              : ! **************************************************************************************************
      85            8 :    SUBROUTINE fit_constrained_density(qs_env, rho_struct, max_iter, eps_rms, step_size, max_backtrack)
      86              :       TYPE(qs_environment_type), POINTER                 :: qs_env
      87              :       TYPE(qs_rho_type), INTENT(INOUT)                   :: rho_struct
      88              :       INTEGER, INTENT(IN)                                :: max_iter
      89              :       REAL(KIND=dp), INTENT(IN)                          :: eps_rms, step_size
      90              :       INTEGER, INTENT(IN)                                :: max_backtrack
      91              : 
      92            8 :       CALL fit_density_matrix(qs_env, rho_struct, max_iter, eps_rms, step_size, max_backtrack)
      93              : 
      94            8 :    END SUBROUTINE fit_constrained_density
      95              : 
      96              : ! **************************************************************************************************
      97              : !> \brief Reconstructs an AO density matrix using the Fermi matrix of H[n_cube] as a
      98              : !>        fermionic relative-entropy prior.
      99              : !> \param qs_env QS environment
     100              : !> \param rho_struct density structure containing the target density on entry and the fit on exit
     101              : !> \param prior_hamiltonian frozen Kohn-Sham matrix built from the target cube density
     102              : !> \param temperature electronic temperature of the Fermi prior
     103              : !> \param entropy_weight weight of the dimensionless fermionic relative entropy
     104              : !> \param max_iter maximum number of projected-gradient iterations
     105              : !> \param eps_rms requested RMS density residual
     106              : !> \param step_size initial spectral projected-gradient step
     107              : !> \param max_backtrack maximum number of step halvings per iteration
     108              : ! **************************************************************************************************
     109            8 :    SUBROUTINE fit_relative_entropy_density(qs_env, rho_struct, prior_hamiltonian, temperature, &
     110              :                                            entropy_weight, max_iter, eps_rms, step_size, max_backtrack)
     111              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     112              :       TYPE(qs_rho_type), INTENT(INOUT)                   :: rho_struct
     113              :       TYPE(dbcsr_type), INTENT(IN)                       :: prior_hamiltonian
     114              :       REAL(KIND=dp), INTENT(IN)                          :: temperature, entropy_weight
     115              :       INTEGER, INTENT(IN)                                :: max_iter
     116              :       REAL(KIND=dp), INTENT(IN)                          :: eps_rms, step_size
     117              :       INTEGER, INTENT(IN)                                :: max_backtrack
     118              : 
     119              :       CALL fit_density_matrix(qs_env, rho_struct, max_iter, eps_rms, step_size, max_backtrack, &
     120            8 :                               prior_hamiltonian, temperature, entropy_weight)
     121              : 
     122            8 :    END SUBROUTINE fit_relative_entropy_density
     123              : 
     124              : ! **************************************************************************************************
     125              : !> \brief Dense implementation shared by the least-squares and relative-entropy fits.
     126              : !> \param qs_env ...
     127              : !> \param rho_struct ...
     128              : !> \param max_iter ...
     129              : !> \param eps_rms ...
     130              : !> \param step_size ...
     131              : !> \param max_backtrack ...
     132              : !> \param prior_hamiltonian ...
     133              : !> \param temperature ...
     134              : !> \param entropy_weight ...
     135              : ! **************************************************************************************************
     136           16 :    SUBROUTINE fit_density_matrix(qs_env, rho_struct, max_iter, eps_rms, step_size, max_backtrack, &
     137              :                                  prior_hamiltonian, temperature, entropy_weight)
     138              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     139              :       TYPE(qs_rho_type), INTENT(INOUT)                   :: rho_struct
     140              :       INTEGER, INTENT(IN)                                :: max_iter
     141              :       REAL(KIND=dp), INTENT(IN)                          :: eps_rms, step_size
     142              :       INTEGER, INTENT(IN)                                :: max_backtrack
     143              :       TYPE(dbcsr_type), INTENT(IN), OPTIONAL             :: prior_hamiltonian
     144              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: temperature, entropy_weight
     145              : 
     146              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'fit_density_matrix'
     147              : 
     148              :       INTEGER                                            :: backtrack, handle, i, info, iteration, &
     149              :                                                             nao, nelectron, unit_nr
     150              :       LOGICAL                                            :: accepted, converged, do_kpoints, &
     151              :                                                             have_previous, use_relative_entropy
     152              :       REAL(KIND=dp) :: alpha, chemical_potential, commutator_norm, direction_derivative, &
     153              :          direction_norm2, fitted_grid_trace, idempotency_error, matrix_trace, objective, &
     154              :          objective_trial, prior_fraction, prior_log_one_minus, relative_entropy, &
     155              :          relative_entropy_trial, relative_temperature, relative_weight, rms, rms_trial, ss, &
     156              :          step_length, sy
     157           16 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues, prior_eigenvalues, &
     158           16 :                                                             prior_logit_values, prior_occupations
     159           16 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: tot_rho_r
     160              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
     161              :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct
     162              :       TYPE(cp_fm_type) :: density_orth, direction_orth, eigenvectors, gradient_orth, &
     163              :          hamiltonian_orth, overlap_chol, previous_density, previous_gradient, prior_logit, &
     164              :          trial_orth, work1, work2
     165              :       TYPE(cp_logger_type), POINTER                      :: logger
     166              :       TYPE(dbcsr_p_type)                                 :: gradient_ao
     167           16 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s, rho_ao
     168              :       TYPE(dft_control_type), POINTER                    :: dft_control
     169              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     170           16 :       TYPE(pw_c1d_gs_type), DIMENSION(:), POINTER        :: rho_g
     171              :       TYPE(pw_env_type), POINTER                         :: pw_env
     172              :       TYPE(pw_pool_type), POINTER                        :: auxbas_pw_pool
     173              :       TYPE(pw_r3d_rs_type)                               :: residual_rspace, target_rspace
     174           16 :       TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER        :: rho_r
     175              :       TYPE(qs_ks_env_type), POINTER                      :: ks_env
     176              : 
     177           16 :       CALL timeset(routineN, handle)
     178              : 
     179           16 :       use_relative_entropy = PRESENT(prior_hamiltonian)
     180           16 :       CPASSERT(use_relative_entropy .EQV. PRESENT(temperature))
     181           16 :       CPASSERT(use_relative_entropy .EQV. PRESENT(entropy_weight))
     182           16 :       IF (use_relative_entropy) THEN
     183            8 :          relative_temperature = temperature
     184            8 :          relative_weight = entropy_weight
     185            8 :          CPASSERT(relative_temperature > 0.0_dp)
     186            8 :          CPASSERT(relative_weight >= 0.0_dp)
     187              :       ELSE
     188            8 :          relative_temperature = 1.0_dp
     189            8 :          relative_weight = 0.0_dp
     190              :       END IF
     191              : 
     192           16 :       NULLIFY (auxbas_pw_pool, blacs_env, dft_control, fm_struct, gradient_ao%matrix, &
     193           16 :                ks_env, matrix_s, para_env, pw_env, rho_ao, rho_g, rho_r, tot_rho_r)
     194              : 
     195              :       CALL get_qs_env(qs_env, blacs_env=blacs_env, dft_control=dft_control, &
     196              :                       do_kpoints=do_kpoints, ks_env=ks_env, matrix_s=matrix_s, &
     197           16 :                       nelectron_total=nelectron, para_env=para_env, pw_env=pw_env)
     198              : 
     199           16 :       IF (do_kpoints) CPABORT("Harris CUBE_FIT is currently available only at the Gamma point")
     200           16 :       IF (dft_control%nspins /= 1) THEN
     201            0 :          CPABORT("Harris CUBE_FIT currently requires a spin-restricted calculation")
     202              :       END IF
     203           16 :       IF (dft_control%qs_control%gapw) CPABORT("Harris CUBE_FIT currently supports GPW only")
     204              : 
     205           16 :       CALL qs_rho_get(rho_struct, rho_ao=rho_ao, rho_r=rho_r, rho_g=rho_g, tot_rho_r=tot_rho_r)
     206           16 :       CPASSERT(ASSOCIATED(rho_ao) .AND. SIZE(rho_ao) == 1)
     207           16 :       CPASSERT(ASSOCIATED(rho_r) .AND. SIZE(rho_r) == 1)
     208           16 :       CPASSERT(ASSOCIATED(rho_g) .AND. SIZE(rho_g) == 1)
     209              : 
     210           16 :       CALL dbcsr_get_info(matrix_s(1)%matrix, nfullrows_total=nao)
     211           16 :       IF (nelectron < 0 .OR. nelectron > 2*nao) THEN
     212            0 :          CPABORT("Electron count is incompatible with the constrained AO density-matrix bounds")
     213              :       END IF
     214              : 
     215           16 :       CALL pw_env_get(pw_env, auxbas_pw_pool=auxbas_pw_pool)
     216           16 :       CALL auxbas_pw_pool%create_pw(target_rspace)
     217           16 :       CALL auxbas_pw_pool%create_pw(residual_rspace)
     218           16 :       CALL pw_copy(rho_r(1), target_rspace)
     219              : 
     220           16 :       ALLOCATE (gradient_ao%matrix)
     221              :       CALL dbcsr_create(gradient_ao%matrix, template=matrix_s(1)%matrix, &
     222           16 :                         name="Harris density-fit gradient", matrix_type=dbcsr_type_symmetric)
     223           16 :       CALL dbcsr_copy(gradient_ao%matrix, matrix_s(1)%matrix)
     224           16 :       CALL dbcsr_set(gradient_ao%matrix, 0.0_dp)
     225              : 
     226              :       CALL cp_fm_struct_create(fm_struct, context=blacs_env, para_env=para_env, &
     227           16 :                                nrow_global=nao, ncol_global=nao)
     228           16 :       CALL cp_fm_create(overlap_chol, fm_struct, name="density-fit overlap")
     229           16 :       CALL cp_fm_create(density_orth, fm_struct, name="density fit in orthogonal basis")
     230           16 :       CALL cp_fm_create(direction_orth, fm_struct, name="density-fit search direction")
     231           16 :       CALL cp_fm_create(gradient_orth, fm_struct, name="density-fit gradient in orthogonal basis")
     232           16 :       CALL cp_fm_create(previous_density, fm_struct, name="previous density-fit matrix")
     233           16 :       CALL cp_fm_create(previous_gradient, fm_struct, name="previous density-fit gradient")
     234           16 :       CALL cp_fm_create(trial_orth, fm_struct, name="density-fit trial matrix")
     235           16 :       CALL cp_fm_create(eigenvectors, fm_struct, name="density-fit eigenvectors")
     236           16 :       IF (use_relative_entropy) THEN
     237            8 :          CALL cp_fm_create(hamiltonian_orth, fm_struct, name="relative-entropy prior Hamiltonian")
     238            8 :          CALL cp_fm_create(prior_logit, fm_struct, name="relative-entropy prior logit")
     239              :       END IF
     240           16 :       CALL cp_fm_create(work1, fm_struct, name="density-fit work matrix 1")
     241           16 :       CALL cp_fm_create(work2, fm_struct, name="density-fit work matrix 2")
     242           16 :       CALL cp_fm_struct_release(fm_struct)
     243           48 :       ALLOCATE (eigenvalues(nao))
     244           16 :       IF (use_relative_entropy) THEN
     245           32 :          ALLOCATE (prior_eigenvalues(nao), prior_logit_values(nao), prior_occupations(nao))
     246              :       END IF
     247              : 
     248           16 :       CALL copy_dbcsr_to_fm(matrix_s(1)%matrix, overlap_chol)
     249           16 :       CALL cp_fm_uplo_to_full(overlap_chol, work1)
     250           16 :       CALL cp_fm_cholesky_decompose(overlap_chol, info_out=info)
     251           16 :       IF (info /= 0) CPABORT("Overlap Cholesky decomposition failed in Harris CUBE_FIT")
     252              : 
     253              :       ! X = U P U^T for S = U^T U.  Tr(X) is the electron count.
     254           16 :       IF (use_relative_entropy) THEN
     255              :          ! H_tilde = U^-T H[n_cube] U^-1 in the same orthonormal representation.
     256            8 :          CALL copy_dbcsr_to_fm(prior_hamiltonian, work1)
     257            8 :          CALL cp_fm_uplo_to_full(work1, work2)
     258              :          CALL cp_fm_cholesky_restore(work1, nao, overlap_chol, work2, &
     259            8 :                                      "SOLVE", pos="LEFT", transa="T")
     260              :          CALL cp_fm_cholesky_restore(work2, nao, overlap_chol, hamiltonian_orth, &
     261            8 :                                      "SOLVE", pos="RIGHT")
     262            8 :          CALL cp_fm_to_fm(hamiltonian_orth, work1)
     263            8 :          CALL cp_fm_syevd(work1, eigenvectors, prior_eigenvalues)
     264              :          CALL fixed_trace_fermi_occupations(prior_eigenvalues, REAL(nelectron, dp), &
     265            8 :                                             relative_temperature, chemical_potential, prior_occupations)
     266            8 :          CALL matrix_from_eigensystem(eigenvectors, prior_occupations, density_orth, work1)
     267            8 :          prior_log_one_minus = 0.0_dp
     268           48 :          DO i = 1, nao
     269           40 :             prior_fraction = clipped_occupation_fraction(prior_occupations(i))
     270           40 :             prior_logit_values(i) = LOG(prior_fraction/(1.0_dp - prior_fraction))
     271           48 :             prior_log_one_minus = prior_log_one_minus + LOG(1.0_dp - prior_fraction)
     272              :          END DO
     273            8 :          CALL matrix_from_eigensystem(eigenvectors, prior_logit_values, prior_logit, work1)
     274              :       ELSE
     275            8 :          CALL copy_dbcsr_to_fm(rho_ao(1)%matrix, work1)
     276            8 :          CALL cp_fm_uplo_to_full(work1, work2)
     277            8 :          CALL cp_fm_cholesky_restore(work1, nao, overlap_chol, work2, "MULTIPLY", pos="LEFT")
     278              :          CALL cp_fm_cholesky_restore(work2, nao, overlap_chol, density_orth, &
     279            8 :                                      "MULTIPLY", pos="RIGHT", transa="T")
     280              :          CALL project_density_matrix(density_orth, eigenvectors, work1, eigenvalues, &
     281            8 :                                      REAL(nelectron, dp), 2.0_dp)
     282            8 :          chemical_potential = 0.0_dp
     283            8 :          prior_log_one_minus = 0.0_dp
     284              :       END IF
     285           16 :       CALL orthogonal_to_ao(density_orth, overlap_chol, work1, work2, nao)
     286           16 :       CALL copy_fm_to_dbcsr(work2, rho_ao(1)%matrix)
     287              :       CALL evaluate_density_fit(qs_env, ks_env, rho_ao, rho_r, rho_g, tot_rho_r, &
     288           16 :                                 target_rspace, residual_rspace, objective, rms)
     289           16 :       relative_entropy = 0.0_dp
     290           16 :       IF (use_relative_entropy) THEN
     291              :          CALL fermionic_relative_entropy(density_orth, prior_logit, eigenvectors, work1, &
     292            8 :                                          eigenvalues, prior_log_one_minus, relative_entropy)
     293            8 :          objective = objective + relative_weight*relative_entropy
     294              :       END IF
     295              : 
     296           16 :       logger => cp_get_default_logger()
     297           16 :       IF (logger%para_env%is_source()) THEN
     298            8 :          unit_nr = cp_logger_get_default_unit_nr(logger, local=.TRUE.)
     299              :       ELSE
     300              :          unit_nr = -1
     301              :       END IF
     302            8 :       IF (unit_nr > 0) THEN
     303            8 :          IF (use_relative_entropy) THEN
     304              :             WRITE (unit_nr, "(/,T3,A,I0)") &
     305            4 :                "HARRIS| Relative-entropy AO density reconstruction; basis functions: ", nao
     306            4 :             WRITE (unit_nr, "(T3,A,ES16.8)") "HARRIS| Prior electronic temperature [a.u.]: ", &
     307            8 :                relative_temperature
     308            4 :             WRITE (unit_nr, "(T3,A,ES16.8)") "HARRIS| Prior chemical potential [a.u.]: ", &
     309            8 :                chemical_potential
     310            4 :             WRITE (unit_nr, "(T3,A,ES16.8)") "HARRIS| Relative-entropy weight: ", relative_weight
     311            4 :             WRITE (unit_nr, "(T3,A,ES16.8)") "HARRIS| Initial fermionic relative entropy: ", &
     312            8 :                relative_entropy
     313              :          ELSE
     314            4 :             WRITE (unit_nr, "(/,T3,A,I0)") "HARRIS| Constrained AO density fit; basis functions: ", nao
     315              :          END IF
     316            8 :          WRITE (unit_nr, "(T3,A,I0)") "HARRIS| Constrained electron count: ", nelectron
     317            8 :          WRITE (unit_nr, "(T3,A,ES16.8)") "HARRIS| Initial density RMS error: ", rms
     318            8 :          IF (use_relative_entropy) THEN
     319              :             WRITE (unit_nr, "(T3,A)") &
     320            4 :                "HARRIS|  Iteration       RMS error   Relative entropy          Objective       Step"
     321              :          ELSE
     322            4 :             WRITE (unit_nr, "(T3,A)") "HARRIS|  Iteration       RMS error          Objective       Step"
     323              :          END IF
     324              :       END IF
     325              : 
     326           16 :       converged = rms <= eps_rms
     327           16 :       alpha = step_size
     328           16 :       have_previous = .FALSE.
     329           16 :       iteration = 0
     330           48 :       DO WHILE (iteration < max_iter .AND. .NOT. converged)
     331           32 :          iteration = iteration + 1
     332              : 
     333           32 :          CALL dbcsr_set(gradient_ao%matrix, 0.0_dp)
     334              :          CALL integrate_v_rspace(v_rspace=residual_rspace, hmat=gradient_ao, &
     335           32 :                                  qs_env=qs_env, calculate_forces=.FALSE.)
     336           32 :          CALL copy_dbcsr_to_fm(gradient_ao%matrix, work1)
     337           32 :          CALL cp_fm_uplo_to_full(work1, work2)
     338              : 
     339              :          ! dF/dX = U^-T (dF/dP) U^-1.
     340              :          CALL cp_fm_cholesky_restore(work1, nao, overlap_chol, work2, &
     341           32 :                                      "SOLVE", pos="LEFT", transa="T")
     342              :          CALL cp_fm_cholesky_restore(work2, nao, overlap_chol, gradient_orth, &
     343           32 :                                      "SOLVE", pos="RIGHT")
     344              : 
     345           32 :          IF (use_relative_entropy .AND. relative_weight > 0.0_dp) THEN
     346              :             CALL add_relative_entropy_gradient(density_orth, prior_logit, eigenvectors, work1, &
     347           16 :                                                work2, eigenvalues, relative_weight, gradient_orth)
     348              :          END IF
     349              : 
     350              :          ! Barzilai-Borwein spectral step for the convex projected-gradient iteration.
     351           32 :          IF (have_previous) THEN
     352           16 :             CALL cp_fm_to_fm(density_orth, work1)
     353           16 :             CALL cp_fm_scale_and_add(1.0_dp, work1, beta=-1.0_dp, matrix_b=previous_density)
     354           16 :             CALL cp_fm_to_fm(gradient_orth, work2)
     355           16 :             CALL cp_fm_scale_and_add(1.0_dp, work2, beta=-1.0_dp, matrix_b=previous_gradient)
     356           16 :             CALL cp_fm_trace(work1, work1, ss)
     357           16 :             CALL cp_fm_trace(work1, work2, sy)
     358           16 :             IF (sy > 100.0_dp*EPSILON(sy)*MAX(1.0_dp, ss)) THEN
     359           16 :                alpha = MIN(100.0_dp*step_size, MAX(1.0E-6_dp*step_size, ss/sy))
     360              :             END IF
     361              :          END IF
     362           32 :          CALL cp_fm_to_fm(density_orth, previous_density)
     363           32 :          CALL cp_fm_to_fm(gradient_orth, previous_gradient)
     364           32 :          have_previous = .TRUE.
     365              : 
     366              :          ! Project once.  Backtracking then stays on the feasible line segment between
     367              :          ! the accepted matrix and its projected spectral-gradient point.
     368           32 :          CALL cp_fm_to_fm(density_orth, trial_orth)
     369           32 :          CALL cp_fm_scale_and_add(1.0_dp, trial_orth, beta=-alpha, matrix_b=gradient_orth)
     370              :          CALL project_density_matrix(trial_orth, eigenvectors, work1, eigenvalues, &
     371           32 :                                      REAL(nelectron, dp), 2.0_dp)
     372           32 :          CALL cp_fm_to_fm(trial_orth, direction_orth)
     373           32 :          CALL cp_fm_scale_and_add(1.0_dp, direction_orth, beta=-1.0_dp, matrix_b=density_orth)
     374           32 :          CALL cp_fm_trace(gradient_orth, direction_orth, direction_derivative)
     375           32 :          CALL cp_fm_trace(direction_orth, direction_orth, direction_norm2)
     376           32 :          IF (direction_norm2 <= 100.0_dp*EPSILON(direction_norm2)) THEN
     377            0 :             IF (unit_nr > 0) WRITE (unit_nr, "(T3,A)") &
     378            0 :                "HARRIS| Density fit stopped: projected gradient reached a stationary point"
     379              :             converged = .TRUE.
     380              :             EXIT
     381              :          END IF
     382              : 
     383           32 :          accepted = .FALSE.
     384           32 :          step_length = 1.0_dp
     385           32 :          DO backtrack = 0, max_backtrack
     386           32 :             CALL cp_fm_to_fm(density_orth, trial_orth)
     387           32 :             CALL cp_fm_scale_and_add(1.0_dp, trial_orth, beta=step_length, matrix_b=direction_orth)
     388           32 :             CALL orthogonal_to_ao(trial_orth, overlap_chol, work1, work2, nao)
     389           32 :             CALL copy_fm_to_dbcsr(work2, rho_ao(1)%matrix)
     390              :             CALL evaluate_density_fit(qs_env, ks_env, rho_ao, rho_r, rho_g, tot_rho_r, &
     391           32 :                                       target_rspace, residual_rspace, objective_trial, rms_trial)
     392           32 :             relative_entropy_trial = 0.0_dp
     393           32 :             IF (use_relative_entropy) THEN
     394              :                CALL fermionic_relative_entropy(trial_orth, prior_logit, eigenvectors, work1, &
     395           16 :                                                eigenvalues, prior_log_one_minus, relative_entropy_trial)
     396           16 :                objective_trial = objective_trial + relative_weight*relative_entropy_trial
     397              :             END IF
     398           32 :             IF (objective_trial <= objective + 1.0E-4_dp*step_length*direction_derivative) THEN
     399              :                accepted = .TRUE.
     400              :                EXIT
     401              :             END IF
     402           32 :             step_length = 0.5_dp*step_length
     403              :          END DO
     404              : 
     405           32 :          IF (.NOT. accepted) THEN
     406              :             ! Leave both the AO matrix and the grids at the last accepted point.
     407            0 :             CALL orthogonal_to_ao(density_orth, overlap_chol, work1, work2, nao)
     408            0 :             CALL copy_fm_to_dbcsr(work2, rho_ao(1)%matrix)
     409              :             CALL evaluate_density_fit(qs_env, ks_env, rho_ao, rho_r, rho_g, tot_rho_r, &
     410            0 :                                       target_rspace, residual_rspace, objective, rms)
     411            0 :             IF (use_relative_entropy) THEN
     412              :                CALL fermionic_relative_entropy(density_orth, prior_logit, eigenvectors, work1, &
     413            0 :                                                eigenvalues, prior_log_one_minus, relative_entropy)
     414              :                objective = objective + relative_weight*relative_entropy
     415              :             END IF
     416            0 :             IF (unit_nr > 0) WRITE (unit_nr, "(T3,A)") &
     417            0 :                "HARRIS| Density fit stopped: projected line search reached a stationary point"
     418           16 :             converged = .TRUE.
     419              :             EXIT
     420              :          END IF
     421              : 
     422           32 :          CALL cp_fm_to_fm(trial_orth, density_orth)
     423           32 :          objective = objective_trial
     424           32 :          rms = rms_trial
     425           32 :          relative_entropy = relative_entropy_trial
     426           32 :          IF (unit_nr > 0) THEN
     427           16 :             IF (use_relative_entropy) THEN
     428              :                WRITE (unit_nr, "(T3,A,I7,4ES19.9)") &
     429            8 :                   "HARRIS| ", iteration, rms, relative_entropy, objective, alpha*step_length
     430              :             ELSE
     431              :                WRITE (unit_nr, "(T3,A,I7,3ES19.9)") &
     432            8 :                   "HARRIS| ", iteration, rms, objective, alpha*step_length
     433              :             END IF
     434              :          END IF
     435           48 :          converged = rms <= eps_rms
     436              :       END DO
     437              : 
     438           16 :       CALL dbcsr_dot(rho_ao(1)%matrix, matrix_s(1)%matrix, matrix_trace)
     439              :       ! pw_integrate_function performs an MPI reduction and therefore has to be
     440              :       ! called collectively, not only by the rank that owns the output unit.
     441           16 :       fitted_grid_trace = pw_integrate_function(rho_r(1), isign=1)
     442           16 :       commutator_norm = 0.0_dp
     443           16 :       idempotency_error = 0.0_dp
     444           16 :       IF (use_relative_entropy) THEN
     445              :          CALL matrix_commutator_norm(density_orth, hamiltonian_orth, work1, work2, nao, &
     446            8 :                                      commutator_norm)
     447            8 :          CALL density_matrix_idempotency_error(density_orth, work1, work2, nao, idempotency_error)
     448              :       END IF
     449           16 :       IF (unit_nr > 0) THEN
     450            8 :          IF (rms <= eps_rms) THEN
     451            8 :             WRITE (unit_nr, "(T3,A,I0,A)") "HARRIS| Density fit reached its RMS target after ", iteration, " iterations"
     452            0 :          ELSE IF (iteration >= max_iter) THEN
     453            0 :             WRITE (unit_nr, "(T3,A,I0,A)") "HARRIS| Density fit stopped after ", iteration, " iterations"
     454              :          END IF
     455            8 :          WRITE (unit_nr, "(T3,A,ES16.8)") "HARRIS| Final density RMS error: ", rms
     456            8 :          WRITE (unit_nr, "(T3,A,F20.10)") "HARRIS| AO density-matrix electron count: ", matrix_trace
     457            8 :          WRITE (unit_nr, "(T3,A,F20.10)") "HARRIS| Fitted grid electron count: ", fitted_grid_trace
     458            8 :          IF (use_relative_entropy) THEN
     459            4 :             WRITE (unit_nr, "(T3,A,ES16.8)") "HARRIS| Final fermionic relative entropy: ", &
     460            8 :                relative_entropy
     461            4 :             WRITE (unit_nr, "(T3,A,ES16.8)") "HARRIS| Prior-Hamiltonian commutator norm: ", &
     462            8 :                commutator_norm
     463            4 :             WRITE (unit_nr, "(T3,A,ES16.8)") "HARRIS| Occupation idempotency error: ", &
     464            8 :                idempotency_error
     465              :          END IF
     466              :       END IF
     467              : 
     468           16 :       DEALLOCATE (eigenvalues)
     469           16 :       IF (use_relative_entropy) THEN
     470            8 :          DEALLOCATE (prior_eigenvalues, prior_logit_values, prior_occupations)
     471              :       END IF
     472           16 :       CALL cp_fm_release(overlap_chol)
     473           16 :       CALL cp_fm_release(density_orth)
     474           16 :       CALL cp_fm_release(direction_orth)
     475           16 :       CALL cp_fm_release(gradient_orth)
     476           16 :       CALL cp_fm_release(previous_density)
     477           16 :       CALL cp_fm_release(previous_gradient)
     478           16 :       CALL cp_fm_release(trial_orth)
     479           16 :       CALL cp_fm_release(eigenvectors)
     480           16 :       IF (use_relative_entropy) THEN
     481            8 :          CALL cp_fm_release(hamiltonian_orth)
     482            8 :          CALL cp_fm_release(prior_logit)
     483              :       END IF
     484           16 :       CALL cp_fm_release(work1)
     485           16 :       CALL cp_fm_release(work2)
     486           16 :       CALL dbcsr_release(gradient_ao%matrix)
     487           16 :       DEALLOCATE (gradient_ao%matrix)
     488           16 :       CALL auxbas_pw_pool%give_back_pw(residual_rspace)
     489           16 :       CALL auxbas_pw_pool%give_back_pw(target_rspace)
     490              : 
     491           16 :       CALL timestop(handle)
     492              : 
     493          128 :    END SUBROUTINE fit_density_matrix
     494              : 
     495              : ! **************************************************************************************************
     496              : !> \brief Builds fixed-trace, spin-restricted Fermi occupations for a set of eigenvalues.
     497              : !> \param eigenvalues ...
     498              : !> \param trace_target ...
     499              : !> \param temperature ...
     500              : !> \param chemical_potential ...
     501              : !> \param occupations ...
     502              : ! **************************************************************************************************
     503            8 :    SUBROUTINE fixed_trace_fermi_occupations(eigenvalues, trace_target, temperature, &
     504            8 :                                             chemical_potential, occupations)
     505              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: eigenvalues
     506              :       REAL(KIND=dp), INTENT(IN)                          :: trace_target, temperature
     507              :       REAL(KIND=dp), INTENT(OUT)                         :: chemical_potential
     508              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: occupations
     509              : 
     510              :       INTEGER                                            :: i, iteration, n
     511              :       REAL(KIND=dp)                                      :: mu_high, mu_low, trace_value
     512              : 
     513            8 :       n = SIZE(eigenvalues)
     514            8 :       CPASSERT(SIZE(occupations) == n)
     515            8 :       CPASSERT(temperature > 0.0_dp)
     516            8 :       CPASSERT(trace_target >= 0.0_dp .AND. trace_target <= 2.0_dp*REAL(n, dp))
     517              : 
     518            8 :       IF (trace_target <= 0.0_dp) THEN
     519            0 :          occupations = 0.0_dp
     520            0 :          chemical_potential = MINVAL(eigenvalues) - 100.0_dp*temperature
     521            0 :          RETURN
     522            8 :       ELSE IF (trace_target >= 2.0_dp*REAL(n, dp)) THEN
     523            0 :          occupations = 2.0_dp
     524            0 :          chemical_potential = MAXVAL(eigenvalues) + 100.0_dp*temperature
     525            0 :          RETURN
     526              :       END IF
     527              : 
     528           48 :       mu_low = MINVAL(eigenvalues) - 100.0_dp*temperature - 1.0_dp
     529           48 :       mu_high = MAXVAL(eigenvalues) + 100.0_dp*temperature + 1.0_dp
     530          424 :       DO iteration = 1, 200
     531          424 :          chemical_potential = 0.5_dp*(mu_low + mu_high)
     532         2544 :          DO i = 1, n
     533         2544 :             occupations(i) = fermi_occupation(eigenvalues(i), chemical_potential, temperature)
     534              :          END DO
     535         2544 :          trace_value = SUM(occupations)
     536          424 :          IF (trace_value > trace_target) THEN
     537              :             mu_high = chemical_potential
     538              :          ELSE
     539          224 :             mu_low = chemical_potential
     540              :          END IF
     541          424 :          IF (ABS(trace_value - trace_target) <= 1.0E-13_dp*MAX(1.0_dp, trace_target)) EXIT
     542              :       END DO
     543            8 :       chemical_potential = 0.5_dp*(mu_low + mu_high)
     544           48 :       DO i = 1, n
     545           48 :          occupations(i) = fermi_occupation(eigenvalues(i), chemical_potential, temperature)
     546              :       END DO
     547              : 
     548              :    END SUBROUTINE fixed_trace_fermi_occupations
     549              : 
     550              : ! **************************************************************************************************
     551              : !> \brief Numerically stable spin-restricted Fermi occupation.
     552              : !> \param energy ...
     553              : !> \param chemical_potential ...
     554              : !> \param temperature ...
     555              : !> \return ...
     556              : ! **************************************************************************************************
     557         2160 :    PURE FUNCTION fermi_occupation(energy, chemical_potential, temperature) RESULT(occupation)
     558              :       REAL(KIND=dp), INTENT(IN)                          :: energy, chemical_potential, temperature
     559              :       REAL(KIND=dp)                                      :: occupation
     560              : 
     561              :       REAL(KIND=dp)                                      :: x
     562              : 
     563         2160 :       x = (energy - chemical_potential)/temperature
     564         2160 :       IF (x >= 50.0_dp) THEN
     565          160 :          occupation = 2.0_dp*EXP(-x)
     566         2000 :       ELSE IF (x <= -50.0_dp) THEN
     567              :          occupation = 2.0_dp
     568              :       ELSE
     569         2000 :          occupation = 2.0_dp/(1.0_dp + EXP(x))
     570              :       END IF
     571              : 
     572         2160 :    END FUNCTION fermi_occupation
     573              : 
     574              : ! **************************************************************************************************
     575              : !> \brief Converts a spin-restricted occupation to a numerically interior fraction.
     576              : !>
     577              : !> At ordinary electronic temperatures a Fermi occupation can round to exactly zero or two.
     578              : !> Clipping its per-spin fraction keeps the matrix logit finite and, because the same operation
     579              : !> is used for the prior and every trial matrix, preserves a zero relative entropy at the prior.
     580              : !> \param occupation spin-restricted occupation in [0,2]
     581              : !> \return clipped per-spin occupation in (0,1)
     582              : ! **************************************************************************************************
     583          240 :    PURE FUNCTION clipped_occupation_fraction(occupation) RESULT(fraction)
     584              :       REAL(KIND=dp), INTENT(IN)                          :: occupation
     585              :       REAL(KIND=dp)                                      :: fraction
     586              : 
     587              :       REAL(KIND=dp), PARAMETER                           :: occupation_clip = 1.0E-14_dp
     588              : 
     589              :       fraction = MIN(1.0_dp - occupation_clip, &
     590          240 :                      MAX(occupation_clip, 0.5_dp*occupation))
     591              : 
     592          240 :    END FUNCTION clipped_occupation_fraction
     593              : 
     594              : ! **************************************************************************************************
     595              : !> \brief Reconstructs V diag(values) V^T.
     596              : !> \param eigenvectors ...
     597              : !> \param values ...
     598              : !> \param matrix ...
     599              : !> \param work ...
     600              : ! **************************************************************************************************
     601           32 :    SUBROUTINE matrix_from_eigensystem(eigenvectors, values, matrix, work)
     602              :       TYPE(cp_fm_type), INTENT(IN)                       :: eigenvectors
     603              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: values
     604              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: matrix, work
     605              : 
     606              :       INTEGER                                            :: n
     607              : 
     608           32 :       n = SIZE(values)
     609           32 :       CALL cp_fm_to_fm(eigenvectors, work)
     610           32 :       CALL cp_fm_column_scale(work, values)
     611           32 :       CALL parallel_gemm('N', 'T', n, n, n, 1.0_dp, work, eigenvectors, 0.0_dp, matrix)
     612              : 
     613           32 :    END SUBROUTINE matrix_from_eigensystem
     614              : 
     615              : ! **************************************************************************************************
     616              : !> \brief Evaluates the fermionic quantum relative entropy D_F(X/2 || f_0).
     617              : !>
     618              : !> The matrix logit of the representable Fermi prior is precomputed spectrally.
     619              : !> \param density_orth ...
     620              : !> \param prior_logit ...
     621              : !> \param eigenvectors ...
     622              : !> \param work ...
     623              : !> \param eigenvalues ...
     624              : !> \param prior_log_one_minus ...
     625              : !> \param relative_entropy ...
     626              : ! **************************************************************************************************
     627           24 :    SUBROUTINE fermionic_relative_entropy(density_orth, prior_logit, eigenvectors, work, eigenvalues, &
     628              :                                          prior_log_one_minus, relative_entropy)
     629              :       TYPE(cp_fm_type), INTENT(IN)                       :: density_orth, prior_logit
     630              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: eigenvectors, work
     631              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: eigenvalues
     632              :       REAL(KIND=dp), INTENT(IN)                          :: prior_log_one_minus
     633              :       REAL(KIND=dp), INTENT(OUT)                         :: relative_entropy
     634              : 
     635              :       INTEGER                                            :: i
     636              :       REAL(KIND=dp)                                      :: entropy_part, f, trace_xlogit
     637              : 
     638           24 :       CALL cp_fm_to_fm(density_orth, work)
     639           24 :       CALL cp_fm_syevd(work, eigenvectors, eigenvalues)
     640           24 :       entropy_part = 0.0_dp
     641          144 :       DO i = 1, SIZE(eigenvalues)
     642          120 :          f = clipped_occupation_fraction(eigenvalues(i))
     643          144 :          entropy_part = entropy_part + f*LOG(f) + (1.0_dp - f)*LOG(1.0_dp - f)
     644              :       END DO
     645           24 :       CALL cp_fm_trace(density_orth, prior_logit, trace_xlogit)
     646           24 :       relative_entropy = entropy_part - prior_log_one_minus - 0.5_dp*trace_xlogit
     647           24 :       IF (relative_entropy < 0.0_dp .AND. ABS(relative_entropy) < 1.0E-10_dp) relative_entropy = 0.0_dp
     648              : 
     649           24 :    END SUBROUTINE fermionic_relative_entropy
     650              : 
     651              : ! **************************************************************************************************
     652              : !> \brief Adds weight*dD_F/dX to a density-fit gradient at fixed trace.
     653              : !> \param density_orth ...
     654              : !> \param prior_logit ...
     655              : !> \param eigenvectors ...
     656              : !> \param work1 ...
     657              : !> \param work2 ...
     658              : !> \param eigenvalues ...
     659              : !> \param weight ...
     660              : !> \param gradient ...
     661              : ! **************************************************************************************************
     662           16 :    SUBROUTINE add_relative_entropy_gradient(density_orth, prior_logit, eigenvectors, &
     663           16 :                                             work1, work2, eigenvalues, weight, gradient)
     664              :       TYPE(cp_fm_type), INTENT(IN)                       :: density_orth, prior_logit
     665              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: eigenvectors, work1, work2
     666              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: eigenvalues
     667              :       REAL(KIND=dp), INTENT(IN)                          :: weight
     668              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: gradient
     669              : 
     670              :       INTEGER                                            :: i
     671              :       REAL(KIND=dp)                                      :: f
     672              : 
     673           16 :       CALL cp_fm_to_fm(density_orth, work1)
     674           16 :       CALL cp_fm_syevd(work1, eigenvectors, eigenvalues)
     675           96 :       DO i = 1, SIZE(eigenvalues)
     676           80 :          f = clipped_occupation_fraction(eigenvalues(i))
     677           96 :          eigenvalues(i) = 0.5_dp*LOG(f/(1.0_dp - f))
     678              :       END DO
     679           16 :       CALL matrix_from_eigensystem(eigenvectors, eigenvalues, work1, work2)
     680           16 :       CALL cp_fm_scale_and_add(1.0_dp, gradient, beta=weight, matrix_b=work1)
     681           16 :       CALL cp_fm_scale_and_add(1.0_dp, gradient, beta=-0.5_dp*weight, matrix_b=prior_logit)
     682              : 
     683           16 :    END SUBROUTINE add_relative_entropy_gradient
     684              : 
     685              : ! **************************************************************************************************
     686              : !> \brief Frobenius norm of the commutator [X,H] in the orthonormal AO representation.
     687              : !> \param density_orth ...
     688              : !> \param hamiltonian_orth ...
     689              : !> \param work1 ...
     690              : !> \param work2 ...
     691              : !> \param nao ...
     692              : !> \param norm ...
     693              : ! **************************************************************************************************
     694           16 :    SUBROUTINE matrix_commutator_norm(density_orth, hamiltonian_orth, work1, work2, nao, norm)
     695              :       TYPE(cp_fm_type), INTENT(IN)                       :: density_orth, hamiltonian_orth
     696              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: work1, work2
     697              :       INTEGER, INTENT(IN)                                :: nao
     698              :       REAL(KIND=dp), INTENT(OUT)                         :: norm
     699              : 
     700              :       REAL(KIND=dp)                                      :: norm_squared
     701              : 
     702              :       CALL parallel_gemm('N', 'N', nao, nao, nao, 1.0_dp, density_orth, hamiltonian_orth, &
     703            8 :                          0.0_dp, work1)
     704              :       CALL parallel_gemm('N', 'N', nao, nao, nao, 1.0_dp, hamiltonian_orth, density_orth, &
     705            8 :                          0.0_dp, work2)
     706            8 :       CALL cp_fm_scale_and_add(1.0_dp, work1, beta=-1.0_dp, matrix_b=work2)
     707            8 :       CALL cp_fm_trace(work1, work1, norm_squared)
     708            8 :       norm = SQRT(MAX(0.0_dp, norm_squared))
     709              : 
     710            8 :    END SUBROUTINE matrix_commutator_norm
     711              : 
     712              : ! **************************************************************************************************
     713              : !> \brief Frobenius norm of f^2-f for f=X/2.
     714              : !> \param density_orth ...
     715              : !> \param work1 ...
     716              : !> \param work2 ...
     717              : !> \param nao ...
     718              : !> \param error ...
     719              : ! **************************************************************************************************
     720           16 :    SUBROUTINE density_matrix_idempotency_error(density_orth, work1, work2, nao, error)
     721              :       TYPE(cp_fm_type), INTENT(IN)                       :: density_orth
     722              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: work1, work2
     723              :       INTEGER, INTENT(IN)                                :: nao
     724              :       REAL(KIND=dp), INTENT(OUT)                         :: error
     725              : 
     726              :       REAL(KIND=dp)                                      :: error_squared
     727              : 
     728              :       CALL parallel_gemm('N', 'N', nao, nao, nao, 1.0_dp, density_orth, density_orth, &
     729            8 :                          0.0_dp, work1)
     730            8 :       CALL cp_fm_to_fm(work1, work2)
     731            8 :       CALL cp_fm_scale_and_add(0.25_dp, work2, beta=-0.5_dp, matrix_b=density_orth)
     732            8 :       CALL cp_fm_trace(work2, work2, error_squared)
     733            8 :       error = SQRT(MAX(0.0_dp, error_squared))
     734              : 
     735            8 :    END SUBROUTINE density_matrix_idempotency_error
     736              : 
     737              : ! **************************************************************************************************
     738              : !> \brief Projects a symmetric matrix onto eigenvalue bounds and a prescribed trace.
     739              : !> \param matrix ...
     740              : !> \param eigenvectors ...
     741              : !> \param work ...
     742              : !> \param eigenvalues ...
     743              : !> \param trace_target ...
     744              : !> \param max_occupation ...
     745              : ! **************************************************************************************************
     746           40 :    SUBROUTINE project_density_matrix(matrix, eigenvectors, work, eigenvalues, trace_target, max_occupation)
     747              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: matrix, eigenvectors, work
     748              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: eigenvalues
     749              :       REAL(KIND=dp), INTENT(IN)                          :: trace_target, max_occupation
     750              : 
     751              :       INTEGER                                            :: i, iteration, n
     752              :       REAL(KIND=dp)                                      :: tau, tau_high, tau_low, trace_value
     753              : 
     754           40 :       n = SIZE(eigenvalues)
     755           40 :       CPASSERT(trace_target >= 0.0_dp .AND. trace_target <= max_occupation*REAL(n, dp))
     756              : 
     757           40 :       CALL cp_fm_syevd(matrix, eigenvectors, eigenvalues)
     758              : 
     759           40 :       IF (trace_target <= 0.0_dp) THEN
     760            0 :          eigenvalues = 0.0_dp
     761           40 :       ELSE IF (trace_target >= max_occupation*REAL(n, dp)) THEN
     762            0 :          eigenvalues = max_occupation
     763              :       ELSE
     764          240 :          tau_low = MINVAL(eigenvalues) - max_occupation
     765          240 :          tau_high = MAXVAL(eigenvalues)
     766         2048 :          DO iteration = 1, 200
     767         2048 :             tau = 0.5_dp*(tau_low + tau_high)
     768        12288 :             trace_value = SUM(MIN(max_occupation, MAX(0.0_dp, eigenvalues - tau)))
     769         2048 :             IF (trace_value > trace_target) THEN
     770              :                tau_low = tau
     771              :             ELSE
     772          880 :                tau_high = tau
     773              :             END IF
     774         2048 :             IF (tau_high - tau_low <= 10.0_dp*EPSILON(tau)*MAX(1.0_dp, ABS(tau))) EXIT
     775              :          END DO
     776           40 :          tau = 0.5_dp*(tau_low + tau_high)
     777          240 :          DO i = 1, n
     778          240 :             eigenvalues(i) = MIN(max_occupation, MAX(0.0_dp, eigenvalues(i) - tau))
     779              :          END DO
     780              :       END IF
     781              : 
     782           40 :       CALL cp_fm_to_fm(eigenvectors, work)
     783           40 :       CALL cp_fm_column_scale(work, eigenvalues)
     784           40 :       CALL parallel_gemm('N', 'T', n, n, n, 1.0_dp, work, eigenvectors, 0.0_dp, matrix)
     785              : 
     786           40 :    END SUBROUTINE project_density_matrix
     787              : 
     788              : ! **************************************************************************************************
     789              : !> \brief Transforms X in the orthonormal representation back to P = U^-1 X U^-T.
     790              : !> \param density_orth ...
     791              : !> \param overlap_chol ...
     792              : !> \param work1 ...
     793              : !> \param density_ao ...
     794              : !> \param nao ...
     795              : ! **************************************************************************************************
     796           48 :    SUBROUTINE orthogonal_to_ao(density_orth, overlap_chol, work1, density_ao, nao)
     797              :       TYPE(cp_fm_type), INTENT(IN)                       :: density_orth, overlap_chol
     798              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: work1, density_ao
     799              :       INTEGER, INTENT(IN)                                :: nao
     800              : 
     801           48 :       CALL cp_fm_cholesky_restore(density_orth, nao, overlap_chol, work1, "SOLVE", pos="LEFT")
     802              :       CALL cp_fm_cholesky_restore(work1, nao, overlap_chol, density_ao, &
     803           48 :                                   "SOLVE", pos="RIGHT", transa="T")
     804              : 
     805           48 :    END SUBROUTINE orthogonal_to_ao
     806              : 
     807              : ! **************************************************************************************************
     808              : !> \brief Collocates the current AO density and evaluates its least-squares residual.
     809              : !> \param qs_env ...
     810              : !> \param ks_env ...
     811              : !> \param rho_ao ...
     812              : !> \param rho_r ...
     813              : !> \param rho_g ...
     814              : !> \param tot_rho_r ...
     815              : !> \param target_rspace ...
     816              : !> \param residual_rspace ...
     817              : !> \param objective ...
     818              : !> \param rms ...
     819              : ! **************************************************************************************************
     820           48 :    SUBROUTINE evaluate_density_fit(qs_env, ks_env, rho_ao, rho_r, rho_g, tot_rho_r, &
     821              :                                    target_rspace, residual_rspace, objective, rms)
     822              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     823              :       TYPE(qs_ks_env_type), POINTER                      :: ks_env
     824              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: rho_ao
     825              :       TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER        :: rho_r
     826              :       TYPE(pw_c1d_gs_type), DIMENSION(:), POINTER        :: rho_g
     827              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: tot_rho_r
     828              :       TYPE(pw_r3d_rs_type), INTENT(IN)                   :: target_rspace
     829              :       TYPE(pw_r3d_rs_type), INTENT(INOUT)                :: residual_rspace
     830              :       REAL(KIND=dp), INTENT(OUT)                         :: objective, rms
     831              : 
     832              :       MARK_USED(qs_env)
     833              : 
     834              :       CALL calculate_rho_elec(matrix_p=rho_ao(1)%matrix, rho=rho_r(1), &
     835           48 :                               rho_gspace=rho_g(1), total_rho=tot_rho_r(1), ks_env=ks_env)
     836           48 :       CALL pw_copy(rho_r(1), residual_rspace)
     837           48 :       CALL pw_axpy(target_rspace, residual_rspace, alpha=-1.0_dp, beta=1.0_dp)
     838           48 :       objective = 0.5_dp*pw_integral_ab(residual_rspace, residual_rspace)
     839           48 :       rms = SQRT(2.0_dp*objective/rho_r(1)%pw_grid%vol)
     840              : 
     841           48 :    END SUBROUTINE evaluate_density_fit
     842              : 
     843              : END MODULE qs_density_fit
        

Generated by: LCOV version 2.0-1