LCOV - code coverage report
Current view: top level - src - qs_scf_subspace.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 70.8 % 264 187
Test Date: 2026-09-03 07:32:15 Functions: 81.8 % 11 9

            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 History management and matrix combination for ADIIS.
      10              : ! **************************************************************************************************
      11              : MODULE qs_scf_subspace
      12              : 
      13              :    USE cp_dbcsr_api,                    ONLY: dbcsr_add,&
      14              :                                               dbcsr_copy,&
      15              :                                               dbcsr_create,&
      16              :                                               dbcsr_p_type,&
      17              :                                               dbcsr_set
      18              :    USE cp_dbcsr_contrib,                ONLY: dbcsr_dot
      19              :    USE ieee_arithmetic,                 ONLY: ieee_is_finite
      20              :    USE kinds,                           ONLY: dp
      21              :    USE qs_scf_subspace_math,            ONLY: qs_scf_subspace_build_adiis_model,&
      22              :                                               qs_scf_subspace_fifo_slot,&
      23              :                                               simplex_qp_nonfinite_input,&
      24              :                                               simplex_qp_pairwise_stationary,&
      25              :                                               simplex_qp_success,&
      26              :                                               simplex_quadratic_minimize
      27              :    USE qs_scf_subspace_types,           ONLY: qs_scf_subspace_buffer_clear,&
      28              :                                               qs_scf_subspace_buffer_type
      29              : #include "./base/base_uses.f90"
      30              : 
      31              :    IMPLICIT NONE
      32              : 
      33              :    PRIVATE
      34              : 
      35              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_scf_subspace'
      36              : 
      37              :    INTEGER, PARAMETER, PUBLIC :: scf_subspace_invalid_history = 100
      38              : 
      39              :    PUBLIC :: qs_scf_subspace_build, qs_scf_subspace_push, qs_scf_subspace_restart
      40              : 
      41              : CONTAINS
      42              : 
      43              : ! **************************************************************************************************
      44              : !> \brief Restart the ADIIS history from the current strictly paired P, F[P] state.
      45              : !> \param buffer Persistent SCF subspace history.
      46              : !> \param fock Current raw F[P], indexed by spin and real-space cell.
      47              : !> \param density Current input P that generated fock, with matching indices.
      48              : !> \param state_energy ...
      49              : !> \param restarted Whether the current state was stored successfully.
      50              : ! **************************************************************************************************
      51            0 :    SUBROUTINE qs_scf_subspace_restart(buffer, fock, density, state_energy, restarted)
      52              : 
      53              :       TYPE(qs_scf_subspace_buffer_type), INTENT(INOUT)   :: buffer
      54              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: fock, density
      55              :       REAL(KIND=dp), INTENT(IN)                          :: state_energy
      56              :       LOGICAL, INTENT(OUT)                               :: restarted
      57              : 
      58            0 :       restarted = .FALSE.
      59            0 :       CALL qs_scf_subspace_buffer_clear(buffer)
      60            0 :       CALL qs_scf_subspace_push(buffer, fock, density, state_energy, restarted)
      61            0 :       IF (.NOT. restarted) RETURN
      62              : 
      63            0 :       buffer%coefficients = 0.0_dp
      64            0 :       buffer%coefficients(1) = 1.0_dp
      65            0 :       buffer%last_status = simplex_qp_success
      66            0 :       buffer%last_restart = .TRUE.
      67            0 :       buffer%use_combined_fock = .FALSE.
      68            0 :       buffer%last_objective = 0.0_dp
      69            0 :       buffer%last_old_fock_weight = 0.0_dp
      70              : 
      71              :    END SUBROUTINE qs_scf_subspace_restart
      72              : 
      73              : ! **************************************************************************************************
      74              : !> \brief Append one accepted, evaluated P,F[P] state to the ADIIS history.
      75              : !>
      76              : !>      The SCF driver owns state acceptance.  Trial endpoints and arbitrary initial guesses
      77              : !>      must not call this routine.
      78              : !> \param buffer Persistent SCF subspace history.
      79              : !> \param fock Current raw F[P], indexed by spin and real-space cell.
      80              : !> \param density Current input P that generated fock, with matching indices.
      81              : !> \param state_energy ...
      82              : !> \param pushed Whether the state was stored successfully.
      83              : ! **************************************************************************************************
      84          420 :    SUBROUTINE qs_scf_subspace_push(buffer, fock, density, state_energy, pushed)
      85              : 
      86              :       TYPE(qs_scf_subspace_buffer_type), INTENT(INOUT)   :: buffer
      87              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: fock, density
      88              :       REAL(KIND=dp), INTENT(IN)                          :: state_energy
      89              :       LOGICAL, INTENT(OUT)                               :: pushed
      90              : 
      91              :       LOGICAL                                            :: storage_valid
      92              : 
      93          140 :       pushed = .FALSE.
      94          140 :       buffer%use_combined_fock = .FALSE.
      95          140 :       buffer%last_restart = .FALSE.
      96          140 :       buffer%last_objective = 0.0_dp
      97          140 :       buffer%last_old_fock_weight = 0.0_dp
      98         2124 :       IF (ALLOCATED(buffer%coefficients)) buffer%coefficients = 0.0_dp
      99              : 
     100          140 :       IF (buffer%nbuffer < 1) THEN
     101            0 :          buffer%last_status = scf_subspace_invalid_history
     102            0 :          RETURN
     103              :       END IF
     104          140 :       IF (buffer%ncall < 0 .OR. buffer%nstored < 0 .OR. buffer%nstored > buffer%nbuffer .OR. &
     105              :           buffer%nstored /= MIN(buffer%ncall, buffer%nbuffer)) THEN
     106            0 :          buffer%last_status = scf_subspace_invalid_history
     107            0 :          RETURN
     108              :       END IF
     109              : 
     110          140 :       CALL ensure_matrix_storage(buffer, fock, density, storage_valid)
     111          140 :       IF (.NOT. storage_valid) THEN
     112            0 :          buffer%last_status = scf_subspace_invalid_history
     113            0 :          RETURN
     114              :       END IF
     115              : 
     116          140 :       CALL push_paired_state(buffer, fock, density, state_energy, pushed)
     117          140 :       IF (.NOT. pushed) THEN
     118            0 :          buffer%last_status = simplex_qp_nonfinite_input
     119            0 :          RETURN
     120              :       END IF
     121          140 :       buffer%last_status = simplex_qp_success
     122              : 
     123              :    END SUBROUTINE qs_scf_subspace_push
     124              : 
     125              : ! **************************************************************************************************
     126              : !> \brief Solve the ADIIS model from previously accepted history and form an effective KS matrix.
     127              : !> \param buffer Persistent SCF subspace history.
     128              : ! **************************************************************************************************
     129          140 :    SUBROUTINE qs_scf_subspace_build(buffer)
     130              : 
     131              :       TYPE(qs_scf_subspace_buffer_type), INTENT(INOUT)   :: buffer
     132              : 
     133              :       INTEGER                                            :: i, j, m, physical
     134          140 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: slots
     135              :       LOGICAL                                            :: model_valid, solver_usable
     136              :       REAL(KIND=dp)                                      :: coefficient_sum, model_delta, &
     137              :                                                             model_tolerance
     138          140 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: difference, linear
     139          140 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: chronological_pf, hessian
     140              : 
     141          140 :       buffer%use_combined_fock = .FALSE.
     142          140 :       buffer%last_restart = .FALSE.
     143          140 :       buffer%last_objective = 0.0_dp
     144          140 :       buffer%last_old_fock_weight = 0.0_dp
     145         2380 :       IF (ALLOCATED(buffer%coefficients)) buffer%coefficients = 0.0_dp
     146              : 
     147          140 :       IF (buffer%nbuffer < 1 .OR. buffer%nstored < 1) THEN
     148            0 :          buffer%last_status = scf_subspace_invalid_history
     149            0 :          RETURN
     150              :       END IF
     151          140 :       IF (buffer%ncall < 0 .OR. buffer%nstored < 0 .OR. buffer%nstored > buffer%nbuffer .OR. &
     152              :           buffer%nstored /= MIN(buffer%ncall, buffer%nbuffer)) THEN
     153            0 :          buffer%last_status = scf_subspace_invalid_history
     154            0 :          RETURN
     155              :       END IF
     156              : 
     157          140 :       IF (.NOT. ASSOCIATED(buffer%fock) .OR. .NOT. ASSOCIATED(buffer%density) .OR. &
     158              :           .NOT. ASSOCIATED(buffer%combined_fock)) THEN
     159            0 :          buffer%last_status = scf_subspace_invalid_history
     160            0 :          RETURN
     161              :       END IF
     162              :       IF (.NOT. ALLOCATED(buffer%generation) .OR. .NOT. ALLOCATED(buffer%pf_metric) .OR. &
     163          140 :           .NOT. ALLOCATED(buffer%coefficients) .OR. .NOT. ALLOCATED(buffer%state_energy)) THEN
     164            0 :          buffer%last_status = scf_subspace_invalid_history
     165            0 :          RETURN
     166              :       END IF
     167              :       IF (SIZE(buffer%generation) /= buffer%nbuffer .OR. &
     168              :           SIZE(buffer%pf_metric, 1) /= buffer%nbuffer .OR. SIZE(buffer%pf_metric, 2) /= buffer%nbuffer .OR. &
     169              :           SIZE(buffer%coefficients) /= buffer%nbuffer .OR. SIZE(buffer%state_energy) /= buffer%nbuffer .OR. &
     170         4620 :           COUNT(buffer%generation > 0) /= buffer%nstored .OR. ANY(buffer%generation < 0)) THEN
     171            0 :          buffer%last_status = scf_subspace_invalid_history
     172            0 :          RETURN
     173              :       END IF
     174              : 
     175          140 :       m = buffer%nstored
     176         1400 :       ALLOCATE (slots(m), chronological_pf(m, m), hessian(m, m), linear(m))
     177          140 :       CALL chronological_slots(buffer, slots)
     178              : 
     179          866 :       DO i = 1, m
     180         5860 :          DO j = 1, m
     181         5720 :             chronological_pf(i, j) = buffer%pf_metric(slots(i), slots(j))
     182              :          END DO
     183              :       END DO
     184              : 
     185          140 :       CALL qs_scf_subspace_build_adiis_model(chronological_pf, m, hessian, linear, model_valid)
     186              : 
     187         2380 :       buffer%coefficients = 0.0_dp
     188          140 :       IF (.NOT. model_valid) THEN
     189            0 :          CALL restart_newest_after_failure(buffer, scf_subspace_invalid_history)
     190            0 :          RETURN
     191              :       END IF
     192              : 
     193              :       CALL simplex_quadratic_minimize(hessian, linear, buffer%coefficients(1:m), &
     194          140 :                                       buffer%last_objective, buffer%last_status, preferred_index=m)
     195              : 
     196              :       solver_usable = buffer%last_status == simplex_qp_success .OR. &
     197          140 :                       buffer%last_status == simplex_qp_pairwise_stationary
     198          140 :       IF (.NOT. solver_usable) THEN
     199            0 :          CALL restart_newest_after_failure(buffer, buffer%last_status)
     200            0 :          RETURN
     201              :       END IF
     202          866 :       IF (.NOT. ieee_is_finite(buffer%last_objective) .OR. &
     203              :           .NOT. ALL(ieee_is_finite(buffer%coefficients(1:m)))) THEN
     204            0 :          CALL restart_newest_after_failure(buffer, simplex_qp_nonfinite_input)
     205            0 :          RETURN
     206              :       END IF
     207              : 
     208          866 :       coefficient_sum = SUM(buffer%coefficients(1:m))
     209          866 :       IF (MINVAL(buffer%coefficients(1:m)) < -100.0_dp*EPSILON(1.0_dp) .OR. &
     210              :           ABS(coefficient_sum - 1.0_dp) > 1000.0_dp*EPSILON(1.0_dp)) THEN
     211            0 :          CALL restart_newest_after_failure(buffer, scf_subspace_invalid_history)
     212            0 :          RETURN
     213              :       END IF
     214              : 
     215          280 :       ALLOCATE (difference(m))
     216          866 :       difference(:) = buffer%coefficients(1:m)
     217          140 :       difference(m) = difference(m) - 1.0_dp
     218          420 :       model_delta = 0.5_dp*DOT_PRODUCT(difference, MATMUL(hessian, difference)) + &
     219        12306 :                     DOT_PRODUCT(difference, hessian(:, m) + linear)
     220              :       model_tolerance = 1000.0_dp*EPSILON(1.0_dp)*REAL(m, KIND=dp)* &
     221         6586 :                         MAX(1.0_dp, MAXVAL(ABS(hessian)), MAXVAL(ABS(linear)))
     222          140 :       IF (.NOT. ieee_is_finite(model_delta) .OR. model_delta > model_tolerance) THEN
     223            0 :          IF (ieee_is_finite(model_delta)) THEN
     224            0 :             CALL restart_newest_after_failure(buffer, scf_subspace_invalid_history)
     225              :          ELSE
     226            0 :             CALL restart_newest_after_failure(buffer, simplex_qp_nonfinite_input)
     227              :          END IF
     228            0 :          RETURN
     229              :       END IF
     230              : 
     231          140 :       buffer%last_old_fock_weight = MAX(0.0_dp, 1.0_dp - buffer%coefficients(m))
     232          140 :       IF (buffer%last_old_fock_weight <= 100.0_dp*EPSILON(1.0_dp)) RETURN
     233              : 
     234           84 :       CALL zero_fock_combination(buffer)
     235          514 :       DO j = 1, m
     236          430 :          physical = slots(j)
     237          430 :          IF (buffer%coefficients(j) <= 0.0_dp) CYCLE
     238          514 :          CALL add_fock_to_combination(buffer, physical, buffer%coefficients(j))
     239              :       END DO
     240           84 :       buffer%use_combined_fock = .TRUE.
     241              : 
     242          280 :    END SUBROUTINE qs_scf_subspace_build
     243              : 
     244              : ! **************************************************************************************************
     245              : !> \brief Retain only the newest accepted state after an unusable ADIIS model.
     246              : !> \param buffer Persistent SCF subspace history.
     247              : !> \param failure_status Status code that caused the restart.
     248              : ! **************************************************************************************************
     249            0 :    SUBROUTINE restart_newest_after_failure(buffer, failure_status)
     250              : 
     251              :       TYPE(qs_scf_subspace_buffer_type), INTENT(INOUT)   :: buffer
     252              :       INTEGER, INTENT(IN)                                :: failure_status
     253              : 
     254              :       INTEGER                                            :: icell, ispin, newest
     255              :       REAL(KIND=dp)                                      :: contribution, newest_energy, newest_pf
     256              : 
     257            0 :       IF (buffer%nstored < 1) THEN
     258            0 :          buffer%last_status = failure_status
     259            0 :          RETURN
     260              :       END IF
     261            0 :       IF (COUNT(buffer%generation > 0) /= buffer%nstored) THEN
     262            0 :          buffer%last_status = failure_status
     263            0 :          RETURN
     264              :       END IF
     265            0 :       newest = MAXLOC(buffer%generation, DIM=1)
     266            0 :       newest_energy = buffer%state_energy(newest)
     267            0 :       newest_pf = 0.0_dp
     268            0 :       DO icell = 1, SIZE(buffer%fock, 3)
     269            0 :          DO ispin = 1, SIZE(buffer%fock, 2)
     270              :             CALL dbcsr_dot(buffer%density(newest, ispin, icell)%matrix, &
     271            0 :                            buffer%fock(newest, ispin, icell)%matrix, contribution)
     272            0 :             newest_pf = newest_pf + contribution
     273              :          END DO
     274              :       END DO
     275            0 :       IF (.NOT. ieee_is_finite(newest_energy) .OR. .NOT. ieee_is_finite(newest_pf)) THEN
     276            0 :          buffer%last_status = failure_status
     277            0 :          RETURN
     278              :       END IF
     279              : 
     280            0 :       buffer%ncall = 1
     281            0 :       buffer%nstored = 1
     282            0 :       buffer%generation = 0
     283            0 :       buffer%generation(newest) = 1
     284            0 :       buffer%state_energy = HUGE(1.0_dp)
     285            0 :       buffer%state_energy(newest) = newest_energy
     286            0 :       buffer%pf_metric = 0.0_dp
     287            0 :       buffer%pf_metric(newest, newest) = newest_pf
     288            0 :       buffer%coefficients = 0.0_dp
     289            0 :       buffer%coefficients(1) = 1.0_dp
     290            0 :       buffer%last_status = failure_status
     291            0 :       buffer%last_restart = .TRUE.
     292            0 :       buffer%use_combined_fock = .FALSE.
     293            0 :       buffer%diis_state_valid = .FALSE.
     294            0 :       buffer%last_objective = 0.0_dp
     295            0 :       buffer%last_old_fock_weight = 0.0_dp
     296            0 :       buffer%diis_weight = 0.0_dp
     297              : 
     298              :    END SUBROUTINE restart_newest_after_failure
     299              : 
     300              : ! **************************************************************************************************
     301              : !> \brief Allocate matrix and scalar storage lazily from current matrix templates.
     302              : !> \param buffer Persistent SCF subspace history.
     303              : !> \param fock Current Fock-matrix templates.
     304              : !> \param density Current density-matrix templates.
     305              : !> \param valid Whether dimensions and existing allocation match.
     306              : ! **************************************************************************************************
     307          140 :    SUBROUTINE ensure_matrix_storage(buffer, fock, density, valid)
     308              : 
     309              :       TYPE(qs_scf_subspace_buffer_type), INTENT(INOUT)   :: buffer
     310              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: fock, density
     311              :       LOGICAL, INTENT(OUT)                               :: valid
     312              : 
     313              :       INTEGER                                            :: icell, islot, ispin, ncell, nspin
     314              : 
     315          140 :       valid = ASSOCIATED(fock) .AND. ASSOCIATED(density)
     316          140 :       IF (.NOT. valid) RETURN
     317          140 :       nspin = SIZE(fock, 1)
     318          140 :       ncell = SIZE(fock, 2)
     319              :       valid = nspin > 0 .AND. ncell > 0 .AND. &
     320          140 :               SIZE(density, 1) == nspin .AND. SIZE(density, 2) == ncell
     321          140 :       IF (.NOT. valid) RETURN
     322         6024 :       DO icell = 1, ncell
     323        16496 :          DO ispin = 1, nspin
     324              :             valid = ASSOCIATED(fock(ispin, icell)%matrix) .AND. &
     325        10472 :                     ASSOCIATED(density(ispin, icell)%matrix)
     326        16356 :             IF (.NOT. valid) RETURN
     327              :          END DO
     328              :       END DO
     329              : 
     330          140 :       IF (ASSOCIATED(buffer%fock)) THEN
     331              :          valid = SIZE(buffer%fock, 1) == buffer%nbuffer .AND. &
     332          124 :                  SIZE(buffer%fock, 2) == nspin .AND. SIZE(buffer%fock, 3) == ncell
     333          124 :          IF (.NOT. valid) RETURN
     334          124 :          valid = ASSOCIATED(buffer%density)
     335          124 :          IF (.NOT. valid) RETURN
     336              :          valid = SIZE(buffer%density, 1) == buffer%nbuffer .AND. &
     337          124 :                  SIZE(buffer%density, 2) == nspin .AND. SIZE(buffer%density, 3) == ncell
     338          124 :          IF (.NOT. valid) RETURN
     339          124 :          valid = ASSOCIATED(buffer%combined_fock)
     340          124 :          IF (.NOT. valid) RETURN
     341              :          valid = SIZE(buffer%combined_fock, 1) == nspin .AND. &
     342          124 :                  SIZE(buffer%combined_fock, 2) == ncell
     343          124 :          IF (.NOT. valid) RETURN
     344              :          valid = ALLOCATED(buffer%generation) .AND. ALLOCATED(buffer%pf_metric) .AND. &
     345          124 :                  ALLOCATED(buffer%coefficients) .AND. ALLOCATED(buffer%state_energy)
     346          124 :          IF (.NOT. valid) RETURN
     347              :          valid = SIZE(buffer%generation) == buffer%nbuffer .AND. &
     348              :                  SIZE(buffer%pf_metric, 1) == buffer%nbuffer .AND. &
     349              :                  SIZE(buffer%pf_metric, 2) == buffer%nbuffer .AND. &
     350              :                  SIZE(buffer%coefficients) == buffer%nbuffer .AND. &
     351          124 :                  SIZE(buffer%state_energy) == buffer%nbuffer
     352          124 :          IF (.NOT. valid) RETURN
     353         5380 :          DO icell = 1, ncell
     354        14740 :             DO ispin = 1, nspin
     355         9360 :                valid = ASSOCIATED(buffer%combined_fock(ispin, icell)%matrix)
     356         9360 :                IF (.NOT. valid) RETURN
     357       164376 :                DO islot = 1, buffer%nbuffer
     358              :                   valid = ASSOCIATED(buffer%fock(islot, ispin, icell)%matrix) .AND. &
     359       149760 :                           ASSOCIATED(buffer%density(islot, ispin, icell)%matrix)
     360       159120 :                   IF (.NOT. valid) RETURN
     361              :                END DO
     362              :             END DO
     363              :          END DO
     364              :          RETURN
     365              :       END IF
     366              : 
     367              :       valid = .NOT. ASSOCIATED(buffer%density) .AND. &
     368              :               .NOT. ASSOCIATED(buffer%combined_fock) .AND. .NOT. ALLOCATED(buffer%generation) .AND. &
     369              :               .NOT. ALLOCATED(buffer%pf_metric) .AND. .NOT. ALLOCATED(buffer%coefficients) .AND. &
     370           16 :               .NOT. ALLOCATED(buffer%state_energy)
     371           16 :       IF (.NOT. valid) RETURN
     372              : 
     373        19612 :       ALLOCATE (buffer%fock(buffer%nbuffer, nspin, ncell))
     374        19596 :       ALLOCATE (buffer%density(buffer%nbuffer, nspin, ncell))
     375         1804 :       ALLOCATE (buffer%combined_fock(nspin, ncell))
     376           80 :       ALLOCATE (buffer%generation(buffer%nbuffer), buffer%state_energy(buffer%nbuffer))
     377           80 :       ALLOCATE (buffer%pf_metric(buffer%nbuffer, buffer%nbuffer), buffer%coefficients(buffer%nbuffer))
     378          272 :       buffer%generation = 0
     379          272 :       buffer%state_energy = HUGE(1.0_dp)
     380         4368 :       buffer%pf_metric = 0.0_dp
     381          272 :       buffer%coefficients = 0.0_dp
     382              : 
     383          644 :       DO icell = 1, ncell
     384         1756 :          DO ispin = 1, nspin
     385        18904 :             DO islot = 1, buffer%nbuffer
     386        17792 :                ALLOCATE (buffer%fock(islot, ispin, icell)%matrix)
     387              :                CALL dbcsr_create(buffer%fock(islot, ispin, icell)%matrix, &
     388        17792 :                                  template=fock(ispin, icell)%matrix)
     389        17792 :                ALLOCATE (buffer%density(islot, ispin, icell)%matrix)
     390              :                CALL dbcsr_create(buffer%density(islot, ispin, icell)%matrix, &
     391        18904 :                                  template=density(ispin, icell)%matrix)
     392              :             END DO
     393         1112 :             ALLOCATE (buffer%combined_fock(ispin, icell)%matrix)
     394              :             CALL dbcsr_create(buffer%combined_fock(ispin, icell)%matrix, &
     395         1740 :                               template=fock(ispin, icell)%matrix)
     396              :          END DO
     397              :       END DO
     398              : 
     399              :    END SUBROUTINE ensure_matrix_storage
     400              : 
     401              : ! **************************************************************************************************
     402              : !> \brief Atomically append one strictly paired P, F[P] state.
     403              : !> \param buffer Persistent SCF subspace history.
     404              : !> \param fock Current raw F[P].
     405              : !> \param density Current P.
     406              : !> \param state_energy ...
     407              : !> \param pushed Whether all scalar checks passed and the state was committed.
     408              : ! **************************************************************************************************
     409          140 :    SUBROUTINE push_paired_state(buffer, fock, density, state_energy, pushed)
     410              : 
     411              :       TYPE(qs_scf_subspace_buffer_type), INTENT(INOUT)   :: buffer
     412              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: fock, density
     413              :       REAL(KIND=dp), INTENT(IN)                          :: state_energy
     414              :       LOGICAL, INTENT(OUT)                               :: pushed
     415              : 
     416              :       INTEGER                                            :: icell, islot, ispin, target_slot
     417              :       LOGICAL                                            :: retained
     418              :       REAL(KIND=dp)                                      :: current_current
     419          140 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: current_old, old_current
     420              : 
     421          140 :       pushed = .FALSE.
     422          140 :       IF (.NOT. ieee_is_finite(state_energy)) RETURN
     423              : 
     424              :       ! Keep the baseline deterministic and auditable: fill empty slots in physical
     425              :       ! order, then evict the oldest accepted state. Energy remains metadata.
     426          140 :       target_slot = qs_scf_subspace_fifo_slot(buffer%generation)
     427          140 :       IF (target_slot <= 0) RETURN
     428              : 
     429          560 :       ALLOCATE (current_old(buffer%nbuffer), old_current(buffer%nbuffer))
     430          140 :       current_old = 0.0_dp
     431          140 :       old_current = 0.0_dp
     432              : 
     433          140 :       CALL paired_matrix_dot(density, fock, current_current)
     434          140 :       IF (.NOT. ieee_is_finite(current_current)) RETURN
     435              : 
     436         2380 :       DO islot = 1, buffer%nbuffer
     437         2826 :          retained = buffer%generation(islot) > 0 .AND. islot /= target_slot
     438              :          IF (.NOT. retained) CYCLE
     439          586 :          CALL history_current_dot(buffer, islot, fock, density, old_current(islot), current_old(islot))
     440          586 :          IF (.NOT. ieee_is_finite(old_current(islot)) .OR. &
     441          140 :              .NOT. ieee_is_finite(current_old(islot))) RETURN
     442              :       END DO
     443              : 
     444         6024 :       DO icell = 1, SIZE(fock, 2)
     445        16496 :          DO ispin = 1, SIZE(fock, 1)
     446        10472 :             CALL dbcsr_copy(buffer%fock(target_slot, ispin, icell)%matrix, fock(ispin, icell)%matrix)
     447        16356 :             CALL dbcsr_copy(buffer%density(target_slot, ispin, icell)%matrix, density(ispin, icell)%matrix)
     448              :          END DO
     449              :       END DO
     450              : 
     451         2380 :       buffer%pf_metric(target_slot, :) = 0.0_dp
     452         2380 :       buffer%pf_metric(:, target_slot) = 0.0_dp
     453          140 :       buffer%pf_metric(target_slot, target_slot) = current_current
     454         2380 :       DO islot = 1, buffer%nbuffer
     455         2826 :          retained = buffer%generation(islot) > 0 .AND. islot /= target_slot
     456              :          IF (.NOT. retained) CYCLE
     457          586 :          buffer%pf_metric(islot, target_slot) = old_current(islot)
     458          726 :          buffer%pf_metric(target_slot, islot) = current_old(islot)
     459              :       END DO
     460              : 
     461          140 :       buffer%ncall = buffer%ncall + 1
     462          140 :       buffer%generation(target_slot) = buffer%ncall
     463          140 :       buffer%state_energy(target_slot) = state_energy
     464          140 :       buffer%nstored = MIN(buffer%nstored + 1, buffer%nbuffer)
     465          140 :       pushed = .TRUE.
     466              : 
     467          280 :    END SUBROUTINE push_paired_state
     468              : 
     469              : ! **************************************************************************************************
     470              : !> \brief Compute Tr(P F), summed across every spin and real-space cell.
     471              : !> \param density Density matrices.
     472              : !> \param fock Fock matrices.
     473              : !> \param value Summed trace contraction.
     474              : ! **************************************************************************************************
     475          140 :    SUBROUTINE paired_matrix_dot(density, fock, value)
     476              : 
     477              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: density, fock
     478              :       REAL(KIND=dp), INTENT(OUT)                         :: value
     479              : 
     480              :       INTEGER                                            :: icell, ispin
     481              :       REAL(KIND=dp)                                      :: contribution
     482              : 
     483          140 :       value = 0.0_dp
     484         6024 :       DO icell = 1, SIZE(fock, 2)
     485        16496 :          DO ispin = 1, SIZE(fock, 1)
     486        10472 :             CALL dbcsr_dot(density(ispin, icell)%matrix, fock(ispin, icell)%matrix, contribution)
     487        16356 :             value = value + contribution
     488              :          END DO
     489              :       END DO
     490              : 
     491          140 :    END SUBROUTINE paired_matrix_dot
     492              : 
     493              : ! **************************************************************************************************
     494              : !> \brief Compute both cross contractions between one old and the current state.
     495              : !> \param buffer Persistent SCF subspace history.
     496              : !> \param islot Physical old-history slot.
     497              : !> \param current_fock Current F[P].
     498              : !> \param current_density Current P.
     499              : !> \param old_current Tr(P_old F_current).
     500              : !> \param current_old Tr(P_current F_old).
     501              : ! **************************************************************************************************
     502          586 :    SUBROUTINE history_current_dot(buffer, islot, current_fock, current_density, old_current, current_old)
     503              : 
     504              :       TYPE(qs_scf_subspace_buffer_type), INTENT(IN)      :: buffer
     505              :       INTEGER, INTENT(IN)                                :: islot
     506              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: current_fock, current_density
     507              :       REAL(KIND=dp), INTENT(OUT)                         :: old_current, current_old
     508              : 
     509              :       INTEGER                                            :: icell, ispin
     510              :       REAL(KIND=dp)                                      :: contribution
     511              : 
     512          586 :       old_current = 0.0_dp
     513          586 :       current_old = 0.0_dp
     514        25328 :       DO icell = 1, SIZE(current_fock, 2)
     515        69628 :          DO ispin = 1, SIZE(current_fock, 1)
     516              :             CALL dbcsr_dot(buffer%density(islot, ispin, icell)%matrix, &
     517        44300 :                            current_fock(ispin, icell)%matrix, contribution)
     518        44300 :             old_current = old_current + contribution
     519              :             CALL dbcsr_dot(current_density(ispin, icell)%matrix, &
     520        44300 :                            buffer%fock(islot, ispin, icell)%matrix, contribution)
     521        69042 :             current_old = current_old + contribution
     522              :          END DO
     523              :       END DO
     524              : 
     525          586 :    END SUBROUTINE history_current_dot
     526              : 
     527              : ! **************************************************************************************************
     528              : !> \brief Return physical slots ordered from oldest to newest.
     529              : !> \param buffer Persistent SCF subspace history.
     530              : !> \param slots Chronologically ordered physical slot indices.
     531              : ! **************************************************************************************************
     532          140 :    PURE SUBROUTINE chronological_slots(buffer, slots)
     533              : 
     534              :       TYPE(qs_scf_subspace_buffer_type), INTENT(IN)      :: buffer
     535              :       INTEGER, DIMENSION(:), INTENT(OUT)                 :: slots
     536              : 
     537              :       INTEGER                                            :: i, islot, j, tmp
     538              : 
     539          140 :       j = 0
     540         2380 :       DO islot = 1, buffer%nbuffer
     541         2240 :          IF (buffer%generation(islot) <= 0) CYCLE
     542          726 :          j = j + 1
     543         2380 :          IF (j <= SIZE(slots)) slots(j) = islot
     544              :       END DO
     545              : 
     546              :       ! The ADIIS model uses the most recent state as its expansion point, so
     547              :       ! return physical slots ordered by insertion generation with the newest
     548              :       ! state last. The history size is small, making insertion sort sufficient.
     549          726 :       DO i = 2, SIZE(slots)
     550          586 :          tmp = slots(i)
     551          586 :          j = i - 1
     552          586 :          DO WHILE (j >= 1)
     553          586 :             IF (buffer%generation(slots(j)) <= buffer%generation(tmp)) EXIT
     554            0 :             slots(j + 1) = slots(j)
     555          586 :             j = j - 1
     556              :          END DO
     557          726 :          slots(j + 1) = tmp
     558              :       END DO
     559              : 
     560          140 :    END SUBROUTINE chronological_slots
     561              : 
     562              : ! **************************************************************************************************
     563              : !> \brief Zero the effective KS-matrix combination.
     564              : !> \param buffer Persistent SCF subspace history and combination target.
     565              : ! **************************************************************************************************
     566           84 :    SUBROUTINE zero_fock_combination(buffer)
     567              : 
     568              :       TYPE(qs_scf_subspace_buffer_type), INTENT(INOUT)   :: buffer
     569              : 
     570              :       INTEGER                                            :: icell, ispin
     571              : 
     572         4076 :       DO icell = 1, SIZE(buffer%combined_fock, 2)
     573        11196 :          DO ispin = 1, SIZE(buffer%combined_fock, 1)
     574        11112 :             CALL dbcsr_set(buffer%combined_fock(ispin, icell)%matrix, 0.0_dp)
     575              :          END DO
     576              :       END DO
     577              : 
     578           84 :    END SUBROUTINE zero_fock_combination
     579              : 
     580              : ! **************************************************************************************************
     581              : !> \brief Add one physical history slot to the effective KS-matrix combination.
     582              : !> \param buffer Persistent SCF subspace history and combination target.
     583              : !> \param physical Physical history slot.
     584              : !> \param coefficient Weight of this history matrix.
     585              : ! **************************************************************************************************
     586          168 :    SUBROUTINE add_fock_to_combination(buffer, physical, coefficient)
     587              : 
     588              :       TYPE(qs_scf_subspace_buffer_type), INTENT(INOUT)   :: buffer
     589              :       INTEGER, INTENT(IN)                                :: physical
     590              :       REAL(KIND=dp), INTENT(IN)                          :: coefficient
     591              : 
     592              :       INTEGER                                            :: icell, ispin
     593              : 
     594         8152 :       DO icell = 1, SIZE(buffer%combined_fock, 2)
     595        22392 :          DO ispin = 1, SIZE(buffer%combined_fock, 1)
     596              :             CALL dbcsr_add(buffer%combined_fock(ispin, icell)%matrix, &
     597              :                            buffer%fock(physical, ispin, icell)%matrix, &
     598        22224 :                            alpha_scalar=1.0_dp, beta_scalar=coefficient)
     599              :          END DO
     600              :       END DO
     601              : 
     602          168 :    END SUBROUTINE add_fock_to_combination
     603              : 
     604          140 : END MODULE qs_scf_subspace
        

Generated by: LCOV version 2.0-1