LCOV - code coverage report
Current view: top level - src - hfx_ace_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 83.2 % 191 159
Test Date: 2026-07-25 06:35:44 Functions: 83.3 % 6 5

            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 Adaptively Compressed Exchange (ACE) operator for HFX.
      10              : !>        Reference: Lin, J. Chem. Theory Comput. 2016, 12, 5, 2242-2249
      11              : !>
      12              : !>  Algorithm (per spin):
      13              : !>
      14              : !>    BUILD  (first call, or every rebuild_frequency steps):
      15              : !>      1. Full HFX: ks_matrix = K_HFX + H_core, energy%ex = E_x(full)
      16              : !>      2. K_AO  = ks_matrix - H_core           (nao x nao, negative semidefinite)
      17              : !>      3. C_occ = first nocc columns of mo_coeff, redistributed to a layout
      18              : !>                 compatible with K_AO so that PDGEMM works correctly
      19              : !>      4. xi    = K_AO * C_occ                 (nao x nocc)
      20              : !>      5. M     = C_occ^T * xi                 (nocc x nocc, negative definite)
      21              : !>      6. -M    = U^T U  via Cholesky          (U upper triangular, stored in M_fm)
      22              : !>      7. W     = xi * U^{-1}                  (nao x nocc, the ACE projector)
      23              : !>      8. Apply (see below) to update ks_matrix and energy%ex
      24              : !>
      25              : !>    APPLY  (all other steps):
      26              : !>      ks_matrix = H_core - W * W^T
      27              : !>      E_x       = -0.5 * Tr[W^T * P * W]
      28              : !>
      29              : !>  Diagnostics (controlled by DBG_STALE / DBG_EXACT_EX module flags):
      30              : !>
      31              : !>    DIAG A  projector staleness  (cheap, runs every APPLY step)
      32              : !>      Computes ||W^T C_occ^current||_F / ||W^T C_occ^BUILD||_F.
      33              : !>      Ratio = 1 → W still accurate.  Ratio -> 0 → W is stale.
      34              : !>
      35              : !>    DIAG B  exact vs ACE exchange energy  (expensive: one full HFX per APPLY)
      36              : !>      Calls full HFX with just_energy=.TRUE. to get E_x^exact[P^k] and
      37              : !>      compares to E_x^ACE[P^k].  Growing |delta| confirms stale W.
      38              : !>      ACE ks_matrix and energy%ex are restored after the diagnostic.
      39              : !>
      40              : !> \author Ritama Kar
      41              : ! **************************************************************************************************
      42              : 
      43              : MODULE hfx_ace_methods
      44              : 
      45              :    USE admm_types,                      ONLY: admm_type,&
      46              :                                               get_admm_env
      47              :    USE bibliography,                    ONLY: Lin2016ACE,&
      48              :                                               cite_reference
      49              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_type
      50              :    USE cp_control_types,                ONLY: dft_control_type
      51              :    USE cp_dbcsr_api,                    ONLY: dbcsr_add,&
      52              :                                               dbcsr_copy,&
      53              :                                               dbcsr_create,&
      54              :                                               dbcsr_p_type,&
      55              :                                               dbcsr_release,&
      56              :                                               dbcsr_set,&
      57              :                                               dbcsr_type
      58              :    USE cp_dbcsr_operations,             ONLY: copy_dbcsr_to_fm,&
      59              :                                               cp_dbcsr_plus_fm_fm_t
      60              :    USE cp_fm_basic_linalg,              ONLY: cp_fm_scale,&
      61              :                                               cp_fm_trace,&
      62              :                                               cp_fm_triangular_multiply
      63              :    USE cp_fm_cholesky,                  ONLY: cp_fm_cholesky_decompose
      64              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      65              :                                               cp_fm_struct_release,&
      66              :                                               cp_fm_struct_type
      67              :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      68              :                                               cp_fm_get_info,&
      69              :                                               cp_fm_release,&
      70              :                                               cp_fm_to_fm,&
      71              :                                               cp_fm_type
      72              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      73              :                                               cp_logger_get_default_io_unit,&
      74              :                                               cp_logger_type
      75              :    USE hfx_admm_utils,                  ONLY: hfx_ks_matrix
      76              :    USE hfx_types,                       ONLY: hfx_type
      77              :    USE input_section_types,             ONLY: section_vals_type
      78              :    USE kinds,                           ONLY: dp
      79              :    USE message_passing,                 ONLY: mp_para_env_type
      80              :    USE parallel_gemm_api,               ONLY: parallel_gemm
      81              :    USE pw_types,                        ONLY: pw_r3d_rs_type
      82              :    USE qs_energy_types,                 ONLY: qs_energy_type
      83              :    USE qs_environment_types,            ONLY: get_qs_env,&
      84              :                                               qs_environment_type
      85              :    USE qs_mo_types,                     ONLY: get_mo_set,&
      86              :                                               mo_set_type
      87              :    USE qs_rho_types,                    ONLY: qs_rho_get,&
      88              :                                               qs_rho_type
      89              :    USE scf_control_types,               ONLY: scf_control_type
      90              : #include "./base/base_uses.f90"
      91              : 
      92              :    IMPLICIT NONE
      93              :    PRIVATE
      94              : 
      95              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'hfx_ace_methods'
      96              : 
      97              :    ! -----------------------------------------------------------------------
      98              :    ! Module-level state: persists across SCF steps within one run.
      99              :    !
     100              :    !   ace_W(1,ispin)   ACE projector W, shape nao x nocc
     101              :    !   ace_is_built     .FALSE. until at least one successful BUILD
     102              :    !   ace_step_counter counts calls since last BUILD
     103              :    !   ace_W_ref_norm   ||W^T C_occ^BUILD||_F stored at BUILD for DIAG A
     104              :    ! -----------------------------------------------------------------------
     105              :    TYPE(cp_fm_type), ALLOCATABLE, SAVE :: ace_W(:, :)
     106              :    LOGICAL, SAVE :: ace_is_built = .FALSE.
     107              :    INTEGER, SAVE :: ace_step_counter = 0
     108              :    REAL(dp), SAVE :: ace_W_ref_norm = 0.0_dp
     109              :    INTEGER, SAVE :: ace_geo_step = 0   ! NEW
     110              : 
     111              :    ! -----------------------------------------------------------------------
     112              :    ! Debug / diagnostic flags  — set all .FALSE. for production.
     113              :    !
     114              :    !   DBG_ROUTING   BUILD/APPLY/DEFER decisions, counters
     115              :    !   DBG_BUILD     norms during BUILD
     116              :    !   DBG_ENERGY    E_x(ACE) at every step; DIAG C on BUILD steps
     117              :    !   DBG_STALE     DIAG A: staleness ratio at every APPLY step (cheap)
     118              :    !   DBG_EXACT_EX  DIAG B: full HFX energy at every APPLY step (expensive)
     119              :    ! -----------------------------------------------------------------------
     120              :    LOGICAL, PARAMETER, PRIVATE :: DBG_ROUTING = .FALSE.
     121              :    LOGICAL, PARAMETER, PRIVATE :: DBG_BUILD = .FALSE.
     122              :    LOGICAL, PARAMETER, PRIVATE :: DBG_ENERGY = .FALSE.
     123              :    LOGICAL, PARAMETER, PRIVATE :: DBG_STALE = .FALSE.
     124              :    LOGICAL, PARAMETER, PRIVATE :: DBG_EXACT_EX = .FALSE.
     125              : 
     126              :    LOGICAL, SAVE :: ace_dynamic_mode = .FALSE.
     127              :    ! Set to .TRUE. by hfx_ace_set_dynamic_mode before geo_opt/MD starts.
     128              :    ! Stays .FALSE. for ENERGY/ENERGY_FORCE single-point runs.
     129              : 
     130              :    PUBLIC :: hfx_ace_ks_matrix, hfx_ace_release, hfx_ace_set_dynamic_mode
     131              : 
     132              : CONTAINS
     133              : 
     134              : ! **************************************************************************************************
     135              : !> \brief Main ACE entry point, replacing hfx_ks_matrix in qs_ks_methods.
     136              : !> \param qs_env ...
     137              : !> \param ks_matrix ...
     138              : !> \param rho ...
     139              : !> \param energy ...
     140              : !> \param calculate_forces ...
     141              : !> \param just_energy ...
     142              : !> \param v_rspace_new ...
     143              : !> \param v_tau_rspace ...
     144              : !> \param ace_rebuild_frequency ...
     145              : !> \param ext_xc_section ...
     146              : ! **************************************************************************************************
     147           48 :    SUBROUTINE hfx_ace_ks_matrix(qs_env, ks_matrix, rho, energy, &
     148              :                                 calculate_forces, just_energy, &
     149              :                                 v_rspace_new, v_tau_rspace, &
     150              :                                 ace_rebuild_frequency, ext_xc_section)
     151              : 
     152              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     153              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: ks_matrix
     154              :       TYPE(qs_rho_type), POINTER                         :: rho
     155              :       TYPE(qs_energy_type), POINTER                      :: energy
     156              :       LOGICAL, INTENT(IN)                                :: calculate_forces, just_energy
     157              :       TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER        :: v_rspace_new, v_tau_rspace
     158              :       INTEGER, INTENT(IN)                                :: ace_rebuild_frequency
     159              :       TYPE(section_vals_type), OPTIONAL, POINTER         :: ext_xc_section
     160              : 
     161              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'hfx_ace_ks_matrix'
     162              : 
     163              :       INTEGER                                            :: handle, iw, n_rep_hf, nspins, &
     164              :                                                             rebuild_freq
     165              :       LOGICAL                                            :: ace_built_now, rebuild_ace
     166              :       REAL(dp)                                           :: ex_ace
     167              :       TYPE(cp_logger_type), POINTER                      :: logger
     168              :       TYPE(dft_control_type), POINTER                    :: dft_control
     169           48 :       TYPE(hfx_type), DIMENSION(:, :), POINTER           :: x_data
     170              :       TYPE(scf_control_type), POINTER                    :: scf_control
     171              : 
     172           48 :       CALL timeset(routineN, handle)
     173              : 
     174           48 :       CALL cite_reference(Lin2016ACE)
     175           48 :       NULLIFY (logger, dft_control, x_data, scf_control)
     176              : 
     177           48 :       logger => cp_get_default_logger()
     178           48 :       iw = cp_logger_get_default_io_unit(logger)
     179              : 
     180           48 :       CALL get_qs_env(qs_env, x_data=x_data, dft_control=dft_control)
     181           48 :       n_rep_hf = SIZE(x_data, 1)
     182           48 :       nspins = dft_control%nspins
     183              : 
     184           48 :       IF (n_rep_hf /= 1) CPABORT("ACE: only one &HF section is supported.")
     185           48 :       IF (dft_control%nimages /= 1) THEN
     186            0 :          CPABORT("ACE: k-points / multiple images are not implemented.")
     187              :       END IF
     188              : 
     189              :       ! ACE requires explicit MO coefficients (C_occ) which are only available
     190              :       ! with diagonalization-based SCF.  OT never constructs mo_coeff during
     191              :       ! the SCF, so the ACE projector build loop would silently get garbage.
     192           48 :       CALL get_qs_env(qs_env, scf_control=scf_control)
     193           48 :       IF (scf_control%use_ot) THEN
     194            0 :          CPABORT("ACE: OT doesn't work, use diagonalization-based SCF.")
     195              :       END IF
     196              : 
     197           48 :       rebuild_freq = MAX(1, ace_rebuild_frequency)
     198              : 
     199              :       ! ------------------------------------------------------------------
     200              :       ! Bypass A: energy-only call
     201              :       ! ------------------------------------------------------------------
     202           48 :       IF (just_energy) THEN
     203              :          IF (DBG_ROUTING .AND. iw > 0) THEN
     204              :             WRITE (iw, '(T2,A)') 'ACE | just_energy=T: full HFX (no matrix update)'
     205              :          END IF
     206              :          CALL hfx_call(qs_env, ks_matrix, rho, energy, &
     207              :                        calculate_forces, just_energy, &
     208            0 :                        v_rspace_new, v_tau_rspace, ext_xc_section)
     209            0 :          CALL timestop(handle)
     210            8 :          RETURN
     211              :       END IF
     212              : 
     213              :       ! ------------------------------------------------------------------
     214              :       ! Bypass B: ionic forces requested
     215              :       ! ------------------------------------------------------------------
     216           48 :       IF (calculate_forces) THEN
     217              :          IF (DBG_ROUTING .AND. iw > 0) THEN
     218              :             WRITE (iw, '(T2,A)') 'ACE | calculate_forces=T: full HFX for exact forces'
     219              :          END IF
     220              :          CALL hfx_call(qs_env, ks_matrix, rho, energy, &
     221              :                        calculate_forces, just_energy, &
     222            8 :                        v_rspace_new, v_tau_rspace, ext_xc_section)
     223            8 :          ace_is_built = .FALSE.
     224            8 :          ace_step_counter = 0
     225            8 :          ace_geo_step = ace_geo_step + 1   ! NEW: step 0 done, ACE active from now
     226            8 :          CALL timestop(handle)
     227            8 :          RETURN
     228              :       END IF
     229              : 
     230              :       ! ------------------------------------------------------------------
     231              :       ! Bypass C: first geometry step.
     232              :       !
     233              :       ! The ATOMIC initial guess gives C_occ far from self-consistency,
     234              :       ! which would produce an inaccurate W.  Running full HFX for the
     235              :       ! entire first geometry step ensures that wavefunction extrapolation
     236              :       ! delivers a near-converged C_occ to geometry step 1, making the
     237              :       ! ACE BUILD there accurate from the first application.
     238              :       ! ------------------------------------------------------------------
     239           40 :       IF (ace_geo_step == 0 .AND. ace_dynamic_mode) THEN
     240            0 :          IF (iw > 0) WRITE (iw, '(T2,A)') &
     241            0 :             'ACE | geo_step=0 (MD/GEO_OPT): full HFX for reference wavefunction'
     242              :          CALL hfx_call(qs_env, ks_matrix, rho, energy, &
     243              :                        .FALSE., just_energy, &
     244            0 :                        v_rspace_new, v_tau_rspace, ext_xc_section)
     245            0 :          CALL timestop(handle)
     246            0 :          RETURN
     247              :       END IF
     248              : 
     249              :       ! ------------------------------------------------------------------
     250              :       ! Rebuild decision
     251              :       ! ------------------------------------------------------------------
     252              :       rebuild_ace = (.NOT. ace_is_built) .OR. &
     253           40 :                     (MOD(ace_step_counter, rebuild_freq) == 0)
     254              : 
     255              :       IF (DBG_ROUTING .AND. iw > 0) THEN
     256              :          WRITE (iw, '(/,T2,A)') REPEAT('-', 56)
     257              :          WRITE (iw, '(T2,A)') 'ACE | hfx_ace_ks_matrix'
     258              :          WRITE (iw, '(T4,A,L1)') 'ace_is_built  = ', ace_is_built
     259              :          WRITE (iw, '(T4,A,L1)') 'rebuild_ace   = ', rebuild_ace
     260              :          WRITE (iw, '(T4,A,I6)') 'step_counter  = ', ace_step_counter
     261              :          WRITE (iw, '(T4,A,I6)') 'rebuild_freq  = ', rebuild_freq
     262              :          WRITE (iw, '(T4,A,I4)') 'nspins        = ', nspins
     263              :          WRITE (iw, '(T4,A)') MERGE('-> BUILD', '-> APPLY', rebuild_ace)
     264              :          WRITE (iw, '(T2,A)') REPEAT('-', 56)
     265              :       END IF
     266              : 
     267           10 :       IF (rebuild_ace) THEN
     268              : 
     269              :          ace_built_now = .FALSE.
     270              :          CALL hfx_ace_build_projector(qs_env, ks_matrix, rho, energy, &
     271              :                                       just_energy, &
     272              :                                       v_rspace_new, v_tau_rspace, &
     273              :                                       nspins, iw, ace_built_now, &
     274           10 :                                       ext_xc_section)
     275           10 :          IF (ace_built_now) THEN
     276            8 :             ace_is_built = .TRUE.
     277            8 :             ace_step_counter = 1
     278              :             IF (DBG_ROUTING .AND. iw > 0) THEN
     279              :                WRITE (iw, '(T4,A)') 'ACE | W built. Projector live from next step.'
     280              :             END IF
     281              :          ELSE
     282            2 :             ace_is_built = .FALSE.
     283            2 :             ace_step_counter = 0
     284              :             IF (DBG_ROUTING .AND. iw > 0) THEN
     285              :                WRITE (iw, '(T4,A)') 'ACE | Build deferred (C_occ=0). Full HFX in ks_matrix.'
     286              :             END IF
     287              :          END IF
     288              : 
     289              :       ELSE
     290              : 
     291           30 :          CALL hfx_ace_apply_projector(qs_env, ks_matrix, rho, energy, nspins, iw)
     292           30 :          ace_step_counter = ace_step_counter + 1
     293              : 
     294              :          ! ----------------------------------------------------------------
     295              :          ! DIAGNOSTIC B: compare E_x^ACE[P^k] with E_x^exact[P^k].
     296              :          !
     297              :          ! Calls full HFX (just_energy=.TRUE.) to get the exact exchange
     298              :          ! energy at the current ACE-converging density P^k.  Compares
     299              :          ! with E_x^ACE[P^k] already stored in energy%ex.
     300              :          !
     301              :          ! Growing |delta| over the SCF confirms the root cause: W was
     302              :          ! built from C_occ^(step 1), which is far from the converged
     303              :          ! C_occ, so K_ACE = -WW^T no longer represents K_x accurately.
     304              :          !
     305              :          ! After the comparison, hfx_ace_apply_projector is called a
     306              :          ! second time to restore ks_matrix and energy%ex to ACE values
     307              :          ! so the SCF continues correctly.  Cost: +2 full HFX per step.
     308              :          ! ----------------------------------------------------------------
     309              :          IF (DBG_EXACT_EX) THEN
     310              :             ex_ace = energy%ex
     311              : 
     312              :             CALL hfx_call(qs_env, ks_matrix, rho, energy, &
     313              :                           .FALSE., .TRUE., &
     314              :                           v_rspace_new, v_tau_rspace, ext_xc_section)
     315              :             IF (iw > 0) THEN
     316              :                WRITE (iw, '(/,T2,A)') REPEAT('-', 56)
     317              :                WRITE (iw, '(T2,A,I6)') 'ACE DIAG B | ace_step_counter = ', ace_step_counter
     318              :                WRITE (iw, '(T4,A,F20.10)') 'E_x(exact, P^k)  = ', energy%ex
     319              :                WRITE (iw, '(T4,A,F20.10)') 'E_x(ACE,   P^k)  = ', ex_ace
     320              :                WRITE (iw, '(T4,A,ES12.4)') '|delta|          = ', ABS(ex_ace - energy%ex)
     321              :                WRITE (iw, '(T4,A)') &
     322              :                   '|delta|->0 on BUILD step; growth confirms stale projector'
     323              :                WRITE (iw, '(T2,A)') REPEAT('-', 56)
     324              :             END IF
     325              : 
     326              :             ! Restore ACE ks_matrix and energy%ex
     327              :             CALL hfx_ace_apply_projector(qs_env, ks_matrix, rho, energy, nspins, iw)
     328              :          END IF
     329              : 
     330              :       END IF
     331              : 
     332              :       IF (DBG_ROUTING .AND. iw > 0) THEN
     333              :          WRITE (iw, '(T4,A,F20.10)') 'energy%ex on exit = ', energy%ex
     334              :          WRITE (iw, '(T4,A,I6)') 'step_counter now  = ', ace_step_counter
     335              :       END IF
     336              : 
     337           40 :       CALL timestop(handle)
     338              : 
     339           48 :    END SUBROUTINE hfx_ace_ks_matrix
     340              : 
     341              : ! **************************************************************************************************
     342              : !> \brief Build the ACE projector W.
     343              : !> \param qs_env ...
     344              : !> \param ks_matrix ...
     345              : !> \param rho ...
     346              : !> \param energy ...
     347              : !> \param just_energy ...
     348              : !> \param v_rspace_new ...
     349              : !> \param v_tau_rspace ...
     350              : !> \param nspins ...
     351              : !> \param iw ...
     352              : !> \param build_succeeded ...
     353              : !> \param ext_xc_section ...
     354              : ! **************************************************************************************************
     355           10 :    SUBROUTINE hfx_ace_build_projector(qs_env, ks_matrix, rho, energy, &
     356              :                                       just_energy, &
     357              :                                       v_rspace_new, v_tau_rspace, &
     358              :                                       nspins, iw, build_succeeded, &
     359              :                                       ext_xc_section)
     360              : 
     361              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     362              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: ks_matrix
     363              :       TYPE(qs_rho_type), POINTER                         :: rho
     364              :       TYPE(qs_energy_type), POINTER                      :: energy
     365              :       LOGICAL, INTENT(IN)                                :: just_energy
     366              :       TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER        :: v_rspace_new, v_tau_rspace
     367              :       INTEGER, INTENT(IN)                                :: nspins, iw
     368              :       LOGICAL, INTENT(OUT)                               :: build_succeeded
     369              :       TYPE(section_vals_type), OPTIONAL, POINTER         :: ext_xc_section
     370              : 
     371              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'hfx_ace_build_projector'
     372              : 
     373              :       INTEGER                                            :: handle, info_chol, ispin, nao, nmo, nocc
     374              :       LOGICAL                                            :: do_admm
     375              :       REAL(dp)                                           :: ehfx_full, frob
     376           10 :       REAL(dp), DIMENSION(:), POINTER                    :: occ_nums
     377              :       TYPE(admm_type), POINTER                           :: admm_env
     378              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
     379              :       TYPE(cp_fm_struct_type), POINTER                   :: fmstruct
     380              :       TYPE(cp_fm_type)                                   :: A_ref_fm, C_occ_fm, K_fm, M_fm, xi_fm
     381              :       TYPE(cp_fm_type), POINTER                          :: mo_coeff
     382           10 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: ks_aux_fit
     383           10 :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: matrix_h
     384              :       TYPE(dbcsr_type)                                   :: K_ao_dbcsr
     385              :       TYPE(dft_control_type), POINTER                    :: dft_control
     386           10 :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos, mos_for_ace
     387              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     388              : 
     389              : ! A_ref_fm: temporary nocc x nocc scratch for DIAG A reference norm
     390              : 
     391           10 :       CALL timeset(routineN, handle)
     392           10 :       NULLIFY (blacs_env, para_env, mos, mo_coeff, matrix_h, occ_nums, fmstruct)
     393           10 :       NULLIFY (dft_control, admm_env, mos_for_ace, ks_aux_fit)
     394              : 
     395           10 :       build_succeeded = .FALSE.
     396              : 
     397              :       CALL get_qs_env(qs_env, blacs_env=blacs_env, para_env=para_env, &
     398              :                       mos=mos, matrix_h_kp=matrix_h, &
     399           10 :                       dft_control=dft_control)
     400              : 
     401           10 :       do_admm = dft_control%do_admm
     402              : 
     403           10 :       IF (do_admm) THEN
     404            6 :          CALL get_qs_env(qs_env, admm_env=admm_env)
     405              :          CALL get_admm_env(admm_env, &
     406              :                            matrix_ks_aux_fit=ks_aux_fit, &
     407            6 :                            mos_aux_fit=mos_for_ace)
     408              :       ELSE
     409            4 :          mos_for_ace => mos
     410              :       END IF
     411              : 
     412              :       ! Step 1: full HFX
     413              :       CALL hfx_call(qs_env, ks_matrix, rho, energy, &
     414              :                     .FALSE., just_energy, &
     415           10 :                     v_rspace_new, v_tau_rspace, ext_xc_section)
     416           10 :       ehfx_full = energy%ex
     417              : 
     418              :       IF (DBG_BUILD .AND. iw > 0) THEN
     419              :          WRITE (iw, '(/,T2,A,F20.10)') 'ACE BUILD | E_x(full HFX) = ', ehfx_full
     420              :       END IF
     421              : 
     422              :       ! Allocate / resize module storage
     423           10 :       IF (ALLOCATED(ace_W)) THEN
     424            2 :          IF (SIZE(ace_W, 2) /= nspins) CALL hfx_ace_release()
     425              :       END IF
     426           42 :       IF (.NOT. ALLOCATED(ace_W)) ALLOCATE (ace_W(1, nspins))
     427              : 
     428              :       ! Reset reference norm; accumulated per spin in the loop below
     429           10 :       ace_W_ref_norm = 0.0_dp
     430              : 
     431              :       ! ----------------------------------------------------------------
     432              :       ! Per-spin build loop
     433              :       ! ----------------------------------------------------------------
     434           18 :       DO ispin = 1, nspins
     435              : 
     436           10 :          IF (mos_for_ace(ispin)%use_mo_coeff_b) THEN
     437              :             CALL copy_dbcsr_to_fm(mos_for_ace(ispin)%mo_coeff_b, &
     438            0 :                                   mos_for_ace(ispin)%mo_coeff)
     439              :          END IF
     440              : 
     441              :          CALL get_mo_set(mos_for_ace(ispin), mo_coeff=mo_coeff, &
     442              :                          nao=nao, nmo=nmo, homo=nocc, &
     443           10 :                          occupation_numbers=occ_nums)
     444              : 
     445           10 :          IF (nocc <= 0) CPABORT("ACE: homo <= 0.")
     446           10 :          IF (nocc > nmo) CPABORT("ACE: homo > nmo.")
     447           10 :          CPASSERT(ASSOCIATED(mo_coeff))
     448              : 
     449           10 :          CALL cp_fm_trace(mo_coeff, mo_coeff, frob)
     450              : 
     451              :          IF (DBG_BUILD .AND. iw > 0) THEN
     452              :             WRITE (iw, '(/,T2,A,I4)') 'ACE BUILD | ispin = ', ispin
     453              :             WRITE (iw, '(T4,A,I8)') 'nao  = ', nao
     454              :             WRITE (iw, '(T4,A,I8)') 'nmo  = ', nmo
     455              :             WRITE (iw, '(T4,A,I8)') 'nocc = ', nocc
     456              :             WRITE (iw, '(T4,A,L1)') 'use_mo_coeff_b = ', mos_for_ace(ispin)%use_mo_coeff_b
     457              :             WRITE (iw, '(T4,A,ES12.4)') '||mo_coeff||_F = ', SQRT(MAX(frob, 0.0_dp))
     458              :          END IF
     459              : 
     460           10 :          IF (frob < 1.0e-20_dp) THEN
     461              :             IF (DBG_BUILD .AND. iw > 0) THEN
     462              :                WRITE (iw, '(T4,A)') 'mo_coeff=0: build deferred to next step.'
     463              :             END IF
     464            2 :             CALL timestop(handle)
     465            2 :             RETURN
     466              :          END IF
     467              : 
     468              :          ! Step 2: K_AO
     469            8 :          IF (do_admm) THEN
     470              :             CALL dbcsr_create(K_ao_dbcsr, template=ks_aux_fit(ispin)%matrix, &
     471            6 :                               name="K_ACE_aux")
     472            6 :             CALL dbcsr_copy(K_ao_dbcsr, ks_aux_fit(ispin)%matrix)
     473              :          ELSE
     474              :             CALL dbcsr_create(K_ao_dbcsr, template=ks_matrix(ispin, 1)%matrix, &
     475            2 :                               name="K_AO")
     476            2 :             CALL dbcsr_copy(K_ao_dbcsr, ks_matrix(ispin, 1)%matrix)
     477            2 :             CALL dbcsr_add(K_ao_dbcsr, matrix_h(1, 1)%matrix, 1.0_dp, -1.0_dp)
     478              :          END IF
     479              : 
     480            8 :          NULLIFY (fmstruct)
     481              :          CALL cp_fm_struct_create(fmstruct, context=blacs_env, para_env=para_env, &
     482            8 :                                   nrow_global=nao, ncol_global=nao)
     483            8 :          CALL cp_fm_create(K_fm, fmstruct, name="K_dense")
     484            8 :          CALL cp_fm_struct_release(fmstruct)
     485            8 :          CALL copy_dbcsr_to_fm(K_ao_dbcsr, K_fm)
     486            8 :          CALL dbcsr_release(K_ao_dbcsr)
     487              : 
     488              :          IF (DBG_BUILD .AND. iw > 0) THEN
     489              :             CALL cp_fm_trace(K_fm, K_fm, frob)
     490              :             WRITE (iw, '(T4,A,ES12.4)') '||K_AO||_F = ', SQRT(MAX(frob, 0.0_dp))
     491              :          END IF
     492              : 
     493              :          ! Step 3: C_occ
     494            8 :          NULLIFY (fmstruct)
     495              :          CALL cp_fm_struct_create(fmstruct, context=blacs_env, para_env=para_env, &
     496            8 :                                   nrow_global=nao, ncol_global=nocc)
     497            8 :          CALL cp_fm_create(C_occ_fm, fmstruct, name="C_occ")
     498            8 :          CALL cp_fm_create(xi_fm, fmstruct, name="xi")
     499            8 :          CALL cp_fm_struct_release(fmstruct)
     500              : 
     501            8 :          CALL cp_fm_to_fm(mo_coeff, C_occ_fm)
     502              : 
     503              :          ! Step 4: xi = K_AO * C_occ
     504              :          CALL parallel_gemm('N', 'N', nao, nocc, nao, &
     505            8 :                             1.0_dp, K_fm, C_occ_fm, 0.0_dp, xi_fm)
     506            8 :          CALL cp_fm_release(K_fm)
     507              : 
     508              :          IF (DBG_BUILD .AND. iw > 0) THEN
     509              :             CALL cp_fm_trace(xi_fm, xi_fm, frob)
     510              :             WRITE (iw, '(T4,A,ES12.4)') '||xi||_F = ', SQRT(MAX(frob, 0.0_dp))
     511              :          END IF
     512              : 
     513              :          ! Step 5: M = C_occ^T * xi
     514            8 :          NULLIFY (fmstruct)
     515              :          CALL cp_fm_struct_create(fmstruct, context=blacs_env, para_env=para_env, &
     516            8 :                                   nrow_global=nocc, ncol_global=nocc)
     517            8 :          CALL cp_fm_create(M_fm, fmstruct, name="M")
     518            8 :          CALL cp_fm_struct_release(fmstruct)
     519              : 
     520              :          CALL parallel_gemm('T', 'N', nocc, nocc, nao, &
     521            8 :                             1.0_dp, C_occ_fm, xi_fm, 0.0_dp, M_fm)
     522            8 :          CALL cp_fm_release(C_occ_fm)
     523              : 
     524              :          IF (DBG_BUILD .AND. iw > 0) THEN
     525              :             CALL cp_fm_trace(M_fm, M_fm, frob)
     526              :             WRITE (iw, '(T4,A,ES12.4)') '||M||_F = ', SQRT(MAX(frob, 0.0_dp))
     527              :          END IF
     528              : 
     529              :          ! Step 6: Cholesky of -M = U^T U
     530            8 :          CALL cp_fm_scale(-1.0_dp, M_fm)
     531            8 :          CALL cp_fm_cholesky_decompose(M_fm, n=nocc, info_out=info_chol)
     532              : 
     533            8 :          IF (info_chol /= 0) THEN
     534            0 :             IF (iw > 0) THEN
     535            0 :                WRITE (iw, '(T4,A,I6)') 'ACE | Cholesky failed, info = ', info_chol
     536            0 :                WRITE (iw, '(T4,A,F20.10)') 'ACE | E_x(full) = ', ehfx_full
     537            0 :                WRITE (iw, '(T4,A,I8,A,I8)') 'ACE | nao=', nao, '  nocc=', nocc
     538              :             END IF
     539            0 :             CPABORT("ACE: Cholesky of -M failed (not positive definite).")
     540              :          END IF
     541              : 
     542              :          IF (DBG_BUILD .AND. iw > 0) THEN
     543              :             WRITE (iw, '(T4,A)') 'Cholesky OK (info=0).'
     544              :          END IF
     545              : 
     546              :          ! Step 7: W = xi * U^{-1}
     547            8 :          IF (ASSOCIATED(ace_W(1, ispin)%matrix_struct)) THEN
     548            0 :             CALL cp_fm_release(ace_W(1, ispin))
     549              :          END IF
     550              : 
     551            8 :          CALL cp_fm_create(ace_W(1, ispin), xi_fm%matrix_struct, name="W_ACE")
     552            8 :          CALL cp_fm_to_fm(xi_fm, ace_W(1, ispin))
     553              : 
     554              :          CALL cp_fm_triangular_multiply(M_fm, ace_W(1, ispin), &
     555              :                                         side='R', uplo_tr='U', &
     556              :                                         transpose_tr=.FALSE., &
     557              :                                         invert_tr=.TRUE., &
     558              :                                         n_rows=nao, n_cols=nocc, &
     559            8 :                                         alpha=1.0_dp)
     560              : 
     561            8 :          CALL cp_fm_release(xi_fm)
     562            8 :          CALL cp_fm_release(M_fm)
     563              : 
     564              :          IF (DBG_BUILD .AND. iw > 0) THEN
     565              :             CALL cp_fm_trace(ace_W(1, ispin), ace_W(1, ispin), frob)
     566              :             WRITE (iw, '(T4,A,I4,A,2I8,A,ES12.4)') &
     567              :                'W spin=', ispin, ' shape=', nao, nocc, &
     568              :                '  ||W||_F=', SQRT(MAX(frob, 0.0_dp))
     569              :          END IF
     570              : 
     571              :          ! ----------------------------------------------------------------
     572              :          ! DIAG A reference: compute ||W^T C_occ^BUILD||_F for this spin.
     573              :          !
     574              :          ! C_occ_fm was released after step 5, but mo_coeff is still valid
     575              :          ! (it is a pointer into mos_for_ace, not allocated here).
     576              :          ! We re-create C_occ_fm from mo_coeff.
     577              :          !
     578              :          ! Theory: W^T C_occ^BUILD = U^{-T}(-U^T U) = -U  →  norm = ||U||_F
     579              :          ! (computed directly rather than storing U).
     580              :          ! ----------------------------------------------------------------
     581           60 :          IF (DBG_STALE) THEN
     582              :             NULLIFY (fmstruct)
     583              :             CALL cp_fm_struct_create(fmstruct, context=blacs_env, &
     584              :                                      para_env=para_env, &
     585              :                                      nrow_global=nao, ncol_global=nocc)
     586              :             CALL cp_fm_create(C_occ_fm, fmstruct, name="C_occ_ref_diag")
     587              :             CALL cp_fm_struct_release(fmstruct)
     588              :             CALL cp_fm_to_fm(mo_coeff, C_occ_fm)
     589              : 
     590              :             NULLIFY (fmstruct)
     591              :             CALL cp_fm_struct_create(fmstruct, context=blacs_env, &
     592              :                                      para_env=para_env, &
     593              :                                      nrow_global=nocc, ncol_global=nocc)
     594              :             CALL cp_fm_create(A_ref_fm, fmstruct, name="WtC_ref")
     595              :             CALL cp_fm_struct_release(fmstruct)
     596              : 
     597              :             CALL parallel_gemm('T', 'N', nocc, nocc, nao, &
     598              :                                1.0_dp, ace_W(1, ispin), C_occ_fm, 0.0_dp, A_ref_fm)
     599              :             CALL cp_fm_trace(A_ref_fm, A_ref_fm, frob)
     600              :             ace_W_ref_norm = ace_W_ref_norm + SQRT(MAX(frob, 0.0_dp))
     601              : 
     602              :             CALL cp_fm_release(C_occ_fm)
     603              :             CALL cp_fm_release(A_ref_fm)
     604              :          END IF
     605              : 
     606              :       END DO  ! ispin
     607              : 
     608              :       IF (DBG_STALE .AND. iw > 0) THEN
     609              :          WRITE (iw, '(/,T2,A)') REPEAT('-', 56)
     610              :          WRITE (iw, '(T2,A)') 'ACE DIAG A | Reference norm stored at BUILD'
     611              :          WRITE (iw, '(T4,A,ES12.4)') &
     612              :             '||W^T C_occ^BUILD||_F (sum over spins) = ', ace_W_ref_norm
     613              :          WRITE (iw, '(T4,A)') &
     614              :             'Staleness ratio = 1.0 at BUILD step; decreasing means W is becoming stale'
     615              :          WRITE (iw, '(T2,A)') REPEAT('-', 56)
     616              :       END IF
     617              : 
     618            8 :       build_succeeded = .TRUE.
     619              : 
     620              :       ! Step 8: apply immediately (DIAG C ratio printed via ehfx_full_ref)
     621              :       CALL hfx_ace_apply_projector(qs_env, ks_matrix, rho, energy, &
     622            8 :                                    nspins, iw, ehfx_full_ref=ehfx_full)
     623              : 
     624            8 :       CALL timestop(handle)
     625              : 
     626           10 :    END SUBROUTINE hfx_ace_build_projector
     627              : 
     628              : ! **************************************************************************************************
     629              : !> \brief Apply the stored ACE projector.
     630              : !> \param qs_env ...
     631              : !> \param ks_matrix ...
     632              : !> \param rho ...
     633              : !> \param energy ...
     634              : !> \param nspins ...
     635              : !> \param iw ...
     636              : !> \param ehfx_full_ref ...
     637              : ! **************************************************************************************************
     638           38 :    SUBROUTINE hfx_ace_apply_projector(qs_env, ks_matrix, rho, energy, &
     639              :                                       nspins, iw, ehfx_full_ref)
     640              : 
     641              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     642              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: ks_matrix
     643              :       TYPE(qs_rho_type), POINTER                         :: rho
     644              :       TYPE(qs_energy_type), POINTER                      :: energy
     645              :       INTEGER, INTENT(IN)                                :: nspins, iw
     646              :       REAL(dp), INTENT(IN), OPTIONAL                     :: ehfx_full_ref
     647              : 
     648              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'hfx_ace_apply_projector'
     649              : 
     650              :       INTEGER                                            :: handle, ispin, nao, nao_d, nmo_d, nocc, &
     651              :                                                             nocc_d
     652              :       LOGICAL                                            :: do_admm
     653              :       REAL(dp)                                           :: ehfx_ace, frob_A, stale_norm, trace_val
     654              :       TYPE(admm_type), POINTER                           :: admm_env
     655              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
     656              :       TYPE(cp_fm_struct_type), POINTER                   :: fmstruct, fmstruct_diag
     657              :       TYPE(cp_fm_type)                                   :: A_diag_fm, C_occ_diag, P_fm, PW_fm
     658              :       TYPE(cp_fm_type), POINTER                          :: mo_coeff_diag
     659           38 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: ks_aux_fit, ks_aux_fit_hfx
     660           38 :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: matrix_h, rho_ao
     661              :       TYPE(dft_control_type), POINTER                    :: dft_control
     662           38 :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos_aux_diag, mos_diag
     663              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     664              :       TYPE(qs_rho_type), POINTER                         :: rho_aux_fit
     665              : 
     666              : ! ------------------------------------------------------------------
     667              : ! DIAG A local variables
     668              : !   stale_norm    ||W^T C_occ^current||_F summed over spins
     669              : !   frob_A        scratch for cp_fm_trace
     670              : !   C_occ_diag    current C_occ redistributed to W layout
     671              : !   A_diag_fm     nocc x nocc overlap  W^T C_occ^current
     672              : !   mos_diag      primary mos (non-ADMM path)
     673              : !   mos_aux_diag  auxiliary mos (ADMM path)
     674              : !   mo_coeff_diag pointer to the relevant mo_coeff
     675              : !   nao_d, nmo_d, nocc_d  dimensions from get_mo_set
     676              : ! ------------------------------------------------------------------
     677              : 
     678           38 :       CALL timeset(routineN, handle)
     679           38 :       NULLIFY (blacs_env, para_env, matrix_h, rho_ao, fmstruct)
     680           38 :       NULLIFY (dft_control, admm_env, ks_aux_fit, ks_aux_fit_hfx, rho_aux_fit)
     681           38 :       NULLIFY (mos_diag, mos_aux_diag, mo_coeff_diag, fmstruct_diag)
     682           38 :       CPASSERT(ALLOCATED(ace_W))
     683              : 
     684              :       CALL get_qs_env(qs_env, blacs_env=blacs_env, para_env=para_env, &
     685           38 :                       matrix_h_kp=matrix_h, dft_control=dft_control)
     686           38 :       do_admm = dft_control%do_admm
     687              : 
     688           38 :       IF (do_admm) THEN
     689           30 :          CALL get_qs_env(qs_env, admm_env=admm_env)
     690              :          CALL get_admm_env(admm_env, &
     691              :                            matrix_ks_aux_fit=ks_aux_fit, &
     692              :                            matrix_ks_aux_fit_hfx=ks_aux_fit_hfx, &
     693           30 :                            rho_aux_fit=rho_aux_fit)
     694           30 :          CALL qs_rho_get(rho_aux_fit, rho_ao_kp=rho_ao)
     695              :       ELSE
     696            8 :          CALL qs_rho_get(rho, rho_ao_kp=rho_ao)
     697              :       END IF
     698              : 
     699           76 :       DO ispin = 1, nspins
     700           38 :          CALL cp_fm_get_info(ace_W(1, ispin), nrow_global=nao, ncol_global=nocc)
     701              : 
     702           76 :          IF (do_admm) THEN
     703           30 :             CALL dbcsr_set(ks_matrix(ispin, 1)%matrix, 0.0_dp)
     704              :             CALL dbcsr_add(ks_matrix(ispin, 1)%matrix, &
     705           30 :                            matrix_h(1, 1)%matrix, 1.0_dp, 1.0_dp)
     706           30 :             CALL dbcsr_set(ks_aux_fit(ispin)%matrix, 0.0_dp)
     707              :             CALL cp_dbcsr_plus_fm_fm_t( &
     708              :                sparse_matrix=ks_aux_fit(ispin)%matrix, &
     709              :                matrix_v=ace_W(1, ispin), &
     710              :                ncol=nocc, &
     711              :                alpha=-1.0_dp, &
     712           30 :                keep_sparsity=.TRUE.)
     713              :             CALL dbcsr_add(ks_aux_fit_hfx(ispin)%matrix, &
     714           30 :                            ks_aux_fit(ispin)%matrix, 0.0_dp, 1.0_dp)
     715              :          ELSE
     716            8 :             CALL dbcsr_set(ks_matrix(ispin, 1)%matrix, 0.0_dp)
     717              :             CALL cp_dbcsr_plus_fm_fm_t( &
     718              :                sparse_matrix=ks_matrix(ispin, 1)%matrix, &
     719              :                matrix_v=ace_W(1, ispin), &
     720              :                ncol=nocc, &
     721              :                alpha=-1.0_dp, &
     722            8 :                keep_sparsity=.TRUE.)
     723              :             CALL dbcsr_add(ks_matrix(ispin, 1)%matrix, &
     724            8 :                            matrix_h(1, 1)%matrix, 1.0_dp, 1.0_dp)
     725              :          END IF
     726              :       END DO
     727              : 
     728              :       ! Exchange energy: E_x = -0.5 * sum_spin Tr[ W^T * P * W ]
     729              :       ehfx_ace = 0.0_dp
     730           76 :       DO ispin = 1, nspins
     731           38 :          CALL cp_fm_get_info(ace_W(1, ispin), nrow_global=nao, ncol_global=nocc)
     732              : 
     733           38 :          NULLIFY (fmstruct)
     734              :          CALL cp_fm_struct_create(fmstruct, context=blacs_env, para_env=para_env, &
     735           38 :                                   nrow_global=nao, ncol_global=nao)
     736           38 :          CALL cp_fm_create(P_fm, fmstruct, name="P_dense")
     737           38 :          CALL cp_fm_struct_release(fmstruct)
     738           38 :          CALL copy_dbcsr_to_fm(rho_ao(ispin, 1)%matrix, P_fm)
     739              : 
     740           38 :          CALL cp_fm_create(PW_fm, ace_W(1, ispin)%matrix_struct, name="PW")
     741              :          CALL parallel_gemm('N', 'N', nao, nocc, nao, &
     742           38 :                             1.0_dp, P_fm, ace_W(1, ispin), 0.0_dp, PW_fm)
     743           38 :          CALL cp_fm_trace(ace_W(1, ispin), PW_fm, trace_val)
     744           38 :          ehfx_ace = ehfx_ace - 0.5_dp*trace_val
     745              : 
     746           38 :          CALL cp_fm_release(P_fm)
     747           38 :          CALL cp_fm_release(PW_fm)
     748              : 
     749          190 :          IF (DBG_ENERGY .AND. iw > 0) THEN
     750              :             WRITE (iw, '(T4,A,I4,A,F20.10)') &
     751              :                'ispin=', ispin, '  E_x(ACE) += ', -0.5_dp*trace_val
     752              :          END IF
     753              :       END DO
     754              : 
     755           38 :       energy%ex = ehfx_ace
     756              : 
     757              :       ! DIAG C: BUILD-step consistency check (printed when ehfx_full_ref present)
     758              :       IF (DBG_ENERGY .AND. iw > 0) THEN
     759              :          WRITE (iw, '(T2,A,F20.10)') 'ACE | E_x(ACE)  = ', ehfx_ace
     760              :          IF (PRESENT(ehfx_full_ref)) THEN
     761              :             WRITE (iw, '(T2,A,F20.10)') 'ACE | E_x(full) = ', ehfx_full_ref
     762              :             WRITE (iw, '(T2,A,ES12.4)') 'ACE | |delta|   = ', ABS(ehfx_ace - ehfx_full_ref)
     763              :             WRITE (iw, '(T2,A)') '(|delta| should be ~0 on BUILD steps; small is good)'
     764              :          END IF
     765              :       END IF
     766              : 
     767              :       ! ----------------------------------------------------------------
     768              :       ! DIAG A: projector staleness check.
     769              :       !
     770              :       ! Computes ||W^T C_occ^current||_F (summed over spins) and divides
     771              :       ! by ace_W_ref_norm = ||W^T C_occ^BUILD||_F stored at BUILD time.
     772              :       !
     773              :       ! staleness_ratio:
     774              :       !   1.0  → C_occ hasn't changed since BUILD; projector is fresh
     775              :       !   < 1  → C_occ has rotated; how much depends on the SCF dynamics
     776              :       !   → 0  → C_occ is orthogonal to the BUILD-time span; W is useless
     777              :       !
     778              :       ! For non-ADMM: C_occ comes from primary mos (nao_orb x nocc).
     779              :       ! For ADMM:     C_occ comes from mos_aux_fit (nao_aux x nocc_aux),
     780              :       !               consistent with ace_W dimensions.
     781              :       ! ----------------------------------------------------------------
     782              :       IF (DBG_STALE .AND. ace_W_ref_norm > 0.0_dp) THEN
     783              :          stale_norm = 0.0_dp
     784              :          CALL get_qs_env(qs_env, mos=mos_diag)
     785              : 
     786              :          DO ispin = 1, nspins
     787              :             CALL cp_fm_get_info(ace_W(1, ispin), nrow_global=nao_d, ncol_global=nocc_d)
     788              : 
     789              :             IF (do_admm) THEN
     790              :                CALL get_admm_env(admm_env, mos_aux_fit=mos_aux_diag)
     791              :                IF (mos_aux_diag(ispin)%use_mo_coeff_b) THEN
     792              :                   CALL copy_dbcsr_to_fm(mos_aux_diag(ispin)%mo_coeff_b, &
     793              :                                         mos_aux_diag(ispin)%mo_coeff)
     794              :                END IF
     795              :                CALL get_mo_set(mos_aux_diag(ispin), mo_coeff=mo_coeff_diag, &
     796              :                                nao=nao_d, nmo=nmo_d, homo=nocc_d)
     797              :             ELSE
     798              :                IF (mos_diag(ispin)%use_mo_coeff_b) THEN
     799              :                   CALL copy_dbcsr_to_fm(mos_diag(ispin)%mo_coeff_b, &
     800              :                                         mos_diag(ispin)%mo_coeff)
     801              :                END IF
     802              :                CALL get_mo_set(mos_diag(ispin), mo_coeff=mo_coeff_diag, &
     803              :                                nao=nao_d, nmo=nmo_d, homo=nocc_d)
     804              :             END IF
     805              : 
     806              :             NULLIFY (fmstruct_diag)
     807              :             CALL cp_fm_struct_create(fmstruct_diag, context=blacs_env, &
     808              :                                      para_env=para_env, &
     809              :                                      nrow_global=nao_d, ncol_global=nocc_d)
     810              :             CALL cp_fm_create(C_occ_diag, fmstruct_diag, name="C_stale")
     811              :             CALL cp_fm_struct_release(fmstruct_diag)
     812              :             CALL cp_fm_to_fm(mo_coeff_diag, C_occ_diag)
     813              : 
     814              :             NULLIFY (fmstruct_diag)
     815              :             CALL cp_fm_struct_create(fmstruct_diag, context=blacs_env, &
     816              :                                      para_env=para_env, &
     817              :                                      nrow_global=nocc_d, ncol_global=nocc_d)
     818              :             CALL cp_fm_create(A_diag_fm, fmstruct_diag, name="WtC_stale")
     819              :             CALL cp_fm_struct_release(fmstruct_diag)
     820              : 
     821              :             CALL parallel_gemm('T', 'N', nocc_d, nocc_d, nao_d, &
     822              :                                1.0_dp, ace_W(1, ispin), C_occ_diag, 0.0_dp, A_diag_fm)
     823              :             CALL cp_fm_trace(A_diag_fm, A_diag_fm, frob_A)
     824              :             stale_norm = stale_norm + SQRT(MAX(frob_A, 0.0_dp))
     825              : 
     826              :             CALL cp_fm_release(C_occ_diag)
     827              :             CALL cp_fm_release(A_diag_fm)
     828              :          END DO
     829              :          IF (iw > 0) THEN
     830              :             WRITE (iw, '(/,T2,A)') REPEAT('-', 56)
     831              :             WRITE (iw, '(T2,A,I6)') 'ACE DIAG A | ace_step_counter      = ', ace_step_counter
     832              :             WRITE (iw, '(T4,A,ES12.4)') '||W^T C_occ^current||_F          = ', stale_norm
     833              :             WRITE (iw, '(T4,A,ES12.4)') '||W^T C_occ^BUILD||_F  (ref)     = ', ace_W_ref_norm
     834              :             WRITE (iw, '(T4,A,F10.6)') 'staleness ratio (1=fresh, 0=stale) = ', &
     835              :                stale_norm/MAX(ace_W_ref_norm, 1.0e-30_dp)
     836              :             WRITE (iw, '(T2,A)') REPEAT('-', 56)
     837              :          END IF
     838              :       END IF
     839              : 
     840           38 :       CALL timestop(handle)
     841              : 
     842           38 :    END SUBROUTINE hfx_ace_apply_projector
     843              : 
     844              : ! **************************************************************************************************
     845              : !> \brief Release all ACE storage and reset state flags.
     846              : !> \param iw_opt ...
     847              : ! **************************************************************************************************
     848            0 :    SUBROUTINE hfx_ace_release(iw_opt)
     849              : 
     850              :       INTEGER, INTENT(IN), OPTIONAL                      :: iw_opt
     851              : 
     852              :       INTEGER                                            :: i, iw, j
     853              : 
     854            0 :       iw = -1
     855            0 :       IF (PRESENT(iw_opt)) iw = iw_opt
     856              : 
     857            0 :       IF (ALLOCATED(ace_W)) THEN
     858            0 :          DO j = 1, SIZE(ace_W, 2)
     859            0 :             DO i = 1, SIZE(ace_W, 1)
     860            0 :                IF (ASSOCIATED(ace_W(i, j)%matrix_struct)) CALL cp_fm_release(ace_W(i, j))
     861              :             END DO
     862              :          END DO
     863            0 :          DEALLOCATE (ace_W)
     864              :       END IF
     865              : 
     866            0 :       ace_is_built = .FALSE.
     867            0 :       ace_step_counter = 0
     868            0 :       ace_W_ref_norm = 0.0_dp
     869            0 :       ace_geo_step = 0
     870            0 :       ace_dynamic_mode = .FALSE.   ! ADDED: reset dynamic mode on release, so it must be explicitly re-enabled for GEO_OPT/MD runs
     871              : 
     872            0 :       IF (iw > 0) WRITE (iw, '(T2,A)') 'ACE | storage released, counters reset'
     873              : 
     874            0 :    END SUBROUTINE hfx_ace_release
     875              : 
     876              :    ! **************************************************************************************************
     877              :    !> \brief Mark this run as dynamic (GEO_OPT/MD) so Bypass C fires for geo step 0.
     878              :    !>        Call this once from the geo_opt or MD driver before the first SCF.
     879              :    !> \param is_dynamic .TRUE. for GEO_OPT/MD, .FALSE. to reset.
     880              :    ! **************************************************************************************************
     881              : ! **************************************************************************************************
     882              : !> \brief ...
     883              : !> \param is_dynamic ...
     884              : ! **************************************************************************************************
     885         2646 :    SUBROUTINE hfx_ace_set_dynamic_mode(is_dynamic)
     886              :       LOGICAL, INTENT(IN)                                :: is_dynamic
     887              : 
     888         2646 :       ace_dynamic_mode = is_dynamic
     889         2646 :    END SUBROUTINE hfx_ace_set_dynamic_mode
     890              : 
     891              :    ! **************************************************************************************************
     892              :    !> \brief Private helper: call hfx_ks_matrix with or without ext_xc_section.
     893              :    !> \param qs_env ...
     894              :    !> \param ks_matrix ...
     895              :    !> \param rho ...
     896              :    !> \param energy ...
     897              :    !> \param calculate_forces ...
     898              :    !> \param just_energy ...
     899              :    !> \param v_rspace_new ...
     900              :    !> \param v_tau_rspace ...
     901              :    !> \param ext_xc_section ...
     902              :    ! **************************************************************************************************
     903              : ! **************************************************************************************************
     904              : !> \brief ...
     905              : !> \param qs_env ...
     906              : !> \param ks_matrix ...
     907              : !> \param rho ...
     908              : !> \param energy ...
     909              : !> \param calculate_forces ...
     910              : !> \param just_energy ...
     911              : !> \param v_rspace_new ...
     912              : !> \param v_tau_rspace ...
     913              : !> \param ext_xc_section ...
     914              : ! **************************************************************************************************
     915           18 :    SUBROUTINE hfx_call(qs_env, ks_matrix, rho, energy, &
     916              :                        calculate_forces, just_energy, &
     917              :                        v_rspace_new, v_tau_rspace, ext_xc_section)
     918              : 
     919              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     920              :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: ks_matrix
     921              :       TYPE(qs_rho_type), POINTER                         :: rho
     922              :       TYPE(qs_energy_type), POINTER                      :: energy
     923              :       LOGICAL, INTENT(IN)                                :: calculate_forces, just_energy
     924              :       TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER        :: v_rspace_new, v_tau_rspace
     925              :       TYPE(section_vals_type), OPTIONAL, POINTER         :: ext_xc_section
     926              : 
     927           18 :       IF (PRESENT(ext_xc_section)) THEN
     928              :          CALL hfx_ks_matrix(qs_env, ks_matrix, rho, energy, &
     929              :                             calculate_forces, just_energy, &
     930              :                             v_rspace_new, v_tau_rspace, &
     931           18 :                             ext_xc_section=ext_xc_section)
     932              :       ELSE
     933              :          CALL hfx_ks_matrix(qs_env, ks_matrix, rho, energy, &
     934              :                             calculate_forces, just_energy, &
     935            0 :                             v_rspace_new, v_tau_rspace)
     936              :       END IF
     937              : 
     938           18 :    END SUBROUTINE hfx_call
     939              : 
     940              : END MODULE hfx_ace_methods
        

Generated by: LCOV version 2.0-1