LCOV - code coverage report
Current view: top level - src - pao_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 98.0 % 347 340
Test Date: 2026-07-25 06:35:44 Functions: 100.0 % 19 19

            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 Methods used by pao_main.F
      10              : !> \author Ole Schuett
      11              : ! **************************************************************************************************
      12              : MODULE pao_methods
      13              :    USE ao_util,                         ONLY: exp_radius
      14              :    USE atomic_kind_types,               ONLY: atomic_kind_type,&
      15              :                                               get_atomic_kind
      16              :    USE basis_set_types,                 ONLY: gto_basis_set_type
      17              :    USE bibliography,                    ONLY: Kolafa2004,&
      18              :                                               Kuhne2007,&
      19              :                                               cite_reference
      20              :    USE cp_control_types,                ONLY: dft_control_type
      21              :    USE cp_dbcsr_api,                    ONLY: &
      22              :         dbcsr_add, dbcsr_binary_read, dbcsr_complete_redistribute, dbcsr_copy, dbcsr_create, &
      23              :         dbcsr_desymmetrize, dbcsr_distribution_get, dbcsr_distribution_new, &
      24              :         dbcsr_distribution_type, dbcsr_filter, dbcsr_get_block_p, dbcsr_get_info, &
      25              :         dbcsr_iterator_blocks_left, dbcsr_iterator_next_block, dbcsr_iterator_start, &
      26              :         dbcsr_iterator_stop, dbcsr_iterator_type, dbcsr_p_type, dbcsr_release, dbcsr_scale, &
      27              :         dbcsr_set, dbcsr_type
      28              :    USE cp_dbcsr_contrib,                ONLY: dbcsr_checksum,&
      29              :                                               dbcsr_dot,&
      30              :                                               dbcsr_reserve_diag_blocks
      31              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      32              :                                               cp_logger_type,&
      33              :                                               cp_to_string
      34              :    USE dm_ls_scf_methods,               ONLY: density_matrix_trs4,&
      35              :                                               ls_scf_init_matrix_S
      36              :    USE dm_ls_scf_qs,                    ONLY: ls_scf_dm_to_ks,&
      37              :                                               ls_scf_qs_atomic_guess,&
      38              :                                               matrix_ls_to_qs,&
      39              :                                               matrix_qs_to_ls
      40              :    USE dm_ls_scf_types,                 ONLY: ls_mstruct_type,&
      41              :                                               ls_scf_env_type
      42              :    USE iterate_matrix,                  ONLY: purify_mcweeny
      43              :    USE kinds,                           ONLY: default_path_length,&
      44              :                                               dp
      45              :    USE machine,                         ONLY: m_walltime
      46              :    USE mathlib,                         ONLY: binomial,&
      47              :                                               diamat_all
      48              :    USE message_passing,                 ONLY: mp_para_env_type
      49              :    USE pao_ml,                          ONLY: pao_ml_forces
      50              :    USE pao_model,                       ONLY: pao_model_forces,&
      51              :                                               pao_model_load
      52              :    USE pao_param,                       ONLY: pao_calc_AB,&
      53              :                                               pao_param_count
      54              :    USE pao_types,                       ONLY: pao_env_type
      55              :    USE particle_types,                  ONLY: particle_type
      56              :    USE qs_energy_types,                 ONLY: qs_energy_type
      57              :    USE qs_environment_types,            ONLY: get_qs_env,&
      58              :                                               qs_environment_type
      59              :    USE qs_initial_guess,                ONLY: calculate_atomic_fock_matrix
      60              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      61              :                                               pao_descriptor_type,&
      62              :                                               pao_potential_type,&
      63              :                                               qs_kind_type,&
      64              :                                               set_qs_kind
      65              :    USE qs_ks_methods,                   ONLY: qs_ks_update_qs_env
      66              :    USE qs_ks_types,                     ONLY: qs_ks_did_change
      67              :    USE qs_rho_methods,                  ONLY: qs_rho_update_rho
      68              :    USE qs_rho_types,                    ONLY: qs_rho_get,&
      69              :                                               qs_rho_type
      70              : 
      71              : !$ USE OMP_LIB, ONLY: omp_get_level
      72              : #include "./base/base_uses.f90"
      73              : 
      74              :    IMPLICIT NONE
      75              : 
      76              :    PRIVATE
      77              : 
      78              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'pao_methods'
      79              : 
      80              :    PUBLIC :: pao_print_atom_info, pao_init_kinds
      81              :    PUBLIC :: pao_build_orthogonalizer, pao_build_selector
      82              :    PUBLIC :: pao_build_diag_distribution
      83              :    PUBLIC :: pao_build_matrix_X, pao_build_core_hamiltonian
      84              :    PUBLIC :: pao_test_convergence
      85              :    PUBLIC :: pao_calc_energy, pao_check_trace_ps
      86              :    PUBLIC :: pao_store_P, pao_add_forces, pao_guess_initial_P
      87              :    PUBLIC :: pao_check_grad
      88              : 
      89              : CONTAINS
      90              : 
      91              : ! **************************************************************************************************
      92              : !> \brief Initialize qs kinds
      93              : !> \param pao ...
      94              : !> \param qs_env ...
      95              : ! **************************************************************************************************
      96           98 :    SUBROUTINE pao_init_kinds(pao, qs_env)
      97              :       TYPE(pao_env_type), POINTER                        :: pao
      98              :       TYPE(qs_environment_type), POINTER                 :: qs_env
      99              : 
     100              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_init_kinds'
     101              : 
     102              :       CHARACTER(LEN=default_path_length)                 :: pao_model_file
     103              :       INTEGER                                            :: handle, i, ikind, pao_basis_size
     104              :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
     105           98 :       TYPE(pao_descriptor_type), DIMENSION(:), POINTER   :: pao_descriptors
     106           98 :       TYPE(pao_potential_type), DIMENSION(:), POINTER    :: pao_potentials
     107           98 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     108              : 
     109           98 :       CALL timeset(routineN, handle)
     110           98 :       CALL get_qs_env(qs_env, qs_kind_set=qs_kind_set)
     111              : 
     112          236 :       DO ikind = 1, SIZE(qs_kind_set)
     113              :          CALL get_qs_kind(qs_kind_set(ikind), &
     114              :                           basis_set=basis_set, &
     115              :                           pao_basis_size=pao_basis_size, &
     116              :                           pao_model_file=pao_model_file, &
     117              :                           pao_potentials=pao_potentials, &
     118          138 :                           pao_descriptors=pao_descriptors)
     119              : 
     120          138 :          IF (pao_basis_size < 1) THEN
     121              :             ! pao disabled for ikind, set pao_basis_size to size of primary basis
     122           12 :             CALL set_qs_kind(qs_kind_set(ikind), pao_basis_size=basis_set%nsgf)
     123              :          END IF
     124              : 
     125              :          ! initialize radii of Gaussians to speedup screeing later on
     126          200 :          DO i = 1, SIZE(pao_potentials)
     127          200 :             pao_potentials(i)%beta_radius = exp_radius(0, pao_potentials(i)%beta, pao%eps_pgf, 1.0_dp)
     128              :          END DO
     129          156 :          DO i = 1, SIZE(pao_descriptors)
     130           18 :             pao_descriptors(i)%beta_radius = exp_radius(0, pao_descriptors(i)%beta, pao%eps_pgf, 1.0_dp)
     131          156 :             pao_descriptors(i)%screening_radius = exp_radius(0, pao_descriptors(i)%screening, pao%eps_pgf, 1.0_dp)
     132              :          END DO
     133              : 
     134              :          ! Load torch model.
     135          374 :          IF (LEN_TRIM(pao_model_file) > 0) THEN
     136            8 :             IF (.NOT. ALLOCATED(pao%models)) THEN
     137           20 :                ALLOCATE (pao%models(SIZE(qs_kind_set)))
     138              :             END IF
     139            8 :             CALL pao_model_load(pao, qs_env, ikind, pao_model_file, pao%models(ikind))
     140              :          END IF
     141              : 
     142              :       END DO
     143           98 :       CALL timestop(handle)
     144           98 :    END SUBROUTINE pao_init_kinds
     145              : 
     146              : ! **************************************************************************************************
     147              : !> \brief Prints a one line summary for each atom.
     148              : !> \param pao ...
     149              : ! **************************************************************************************************
     150           98 :    SUBROUTINE pao_print_atom_info(pao)
     151              :       TYPE(pao_env_type), POINTER                        :: pao
     152              : 
     153              :       INTEGER                                            :: iatom, natoms
     154           98 :       INTEGER, DIMENSION(:), POINTER                     :: pao_basis, param_cols, param_rows, &
     155           98 :                                                             pri_basis
     156              : 
     157           98 :       CALL dbcsr_get_info(pao%matrix_Y, row_blk_size=pri_basis, col_blk_size=pao_basis)
     158           98 :       CPASSERT(SIZE(pao_basis) == SIZE(pri_basis))
     159           98 :       natoms = SIZE(pao_basis)
     160              : 
     161           98 :       CALL dbcsr_get_info(pao%matrix_X, row_blk_size=param_rows, col_blk_size=param_cols)
     162           98 :       CPASSERT(SIZE(param_rows) == natoms .AND. SIZE(param_cols) == natoms)
     163              : 
     164           98 :       IF (pao%iw_atoms > 0) THEN
     165           12 :          DO iatom = 1, natoms
     166              :             WRITE (pao%iw_atoms, "(A,I7,T20,A,I3,T45,A,I3,T65,A,I3)") &
     167            9 :                " PAO| atom: ", iatom, &
     168            9 :                " prim_basis: ", pri_basis(iatom), &
     169            9 :                " pao_basis: ", pao_basis(iatom), &
     170           21 :                " pao_params: ", (param_cols(iatom)*param_rows(iatom))
     171              :          END DO
     172              :       END IF
     173           98 :    END SUBROUTINE pao_print_atom_info
     174              : 
     175              : ! **************************************************************************************************
     176              : !> \brief Constructs matrix_N and its inverse.
     177              : !> \param pao ...
     178              : !> \param qs_env ...
     179              : ! **************************************************************************************************
     180           98 :    SUBROUTINE pao_build_orthogonalizer(pao, qs_env)
     181              :       TYPE(pao_env_type), POINTER                        :: pao
     182              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     183              : 
     184              :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_build_orthogonalizer'
     185              : 
     186              :       INTEGER                                            :: acol, arow, handle, i, iatom, j, k, N
     187              :       LOGICAL                                            :: found
     188              :       REAL(dp)                                           :: v, w
     189           98 :       REAL(dp), DIMENSION(:), POINTER                    :: evals
     190           98 :       REAL(dp), DIMENSION(:, :), POINTER                 :: A, block_N, block_N_inv, block_S
     191              :       TYPE(dbcsr_iterator_type)                          :: iter
     192           98 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     193              : 
     194           98 :       CALL timeset(routineN, handle)
     195              : 
     196           98 :       CALL get_qs_env(qs_env, matrix_s=matrix_s)
     197              : 
     198           98 :       CALL dbcsr_create(pao%matrix_N, template=matrix_s(1)%matrix, name="PAO matrix_N")
     199           98 :       CALL dbcsr_reserve_diag_blocks(pao%matrix_N)
     200              : 
     201           98 :       CALL dbcsr_create(pao%matrix_N_inv, template=matrix_s(1)%matrix, name="PAO matrix_N_inv")
     202           98 :       CALL dbcsr_reserve_diag_blocks(pao%matrix_N_inv)
     203              : 
     204              : !$OMP PARALLEL DEFAULT(NONE) SHARED(pao,matrix_s) &
     205           98 : !$OMP PRIVATE(iter,arow,acol,iatom,block_N,block_N_inv,block_S,found,N,A,evals,k,i,j,w,v)
     206              :       CALL dbcsr_iterator_start(iter, pao%matrix_N)
     207              :       DO WHILE (dbcsr_iterator_blocks_left(iter))
     208              :          CALL dbcsr_iterator_next_block(iter, arow, acol, block_N)
     209              :          iatom = arow; CPASSERT(arow == acol)
     210              : 
     211              :          CALL dbcsr_get_block_p(matrix=pao%matrix_N_inv, row=iatom, col=iatom, block=block_N_inv, found=found)
     212              :          CPASSERT(ASSOCIATED(block_N_inv))
     213              : 
     214              :          CALL dbcsr_get_block_p(matrix=matrix_s(1)%matrix, row=iatom, col=iatom, block=block_S, found=found)
     215              :          CPASSERT(ASSOCIATED(block_S))
     216              : 
     217              :          N = SIZE(block_S, 1); CPASSERT(SIZE(block_S, 1) == SIZE(block_S, 2)) ! primary basis size
     218              :          ALLOCATE (A(N, N), evals(N))
     219              : 
     220              :          ! take square root of atomic overlap matrix
     221              :          A = block_S
     222              :          CALL diamat_all(A, evals) !afterwards A contains the eigenvectors
     223              :          block_N = 0.0_dp
     224              :          block_N_inv = 0.0_dp
     225              :          DO k = 1, N
     226              :             ! NOTE: To maintain a consistent notation with the Berghold paper,
     227              :             ! the "_inv" is swapped: N^{-1}=sqrt(S); N=sqrt(S)^{-1}
     228              :             w = 1.0_dp/SQRT(evals(k))
     229              :             v = SQRT(evals(k))
     230              :             DO i = 1, N
     231              :                DO j = 1, N
     232              :                   block_N(i, j) = block_N(i, j) + w*A(i, k)*A(j, k)
     233              :                   block_N_inv(i, j) = block_N_inv(i, j) + v*A(i, k)*A(j, k)
     234              :                END DO
     235              :             END DO
     236              :          END DO
     237              :          DEALLOCATE (A, evals)
     238              :       END DO
     239              :       CALL dbcsr_iterator_stop(iter)
     240              : !$OMP END PARALLEL
     241              : 
     242              :       ! store a copies of N and N_inv that are distributed according to pao%diag_distribution
     243              :       CALL dbcsr_create(pao%matrix_N_diag, &
     244              :                         name="PAO matrix_N_diag", &
     245              :                         dist=pao%diag_distribution, &
     246           98 :                         template=matrix_s(1)%matrix)
     247           98 :       CALL dbcsr_reserve_diag_blocks(pao%matrix_N_diag)
     248           98 :       CALL dbcsr_complete_redistribute(pao%matrix_N, pao%matrix_N_diag)
     249              :       CALL dbcsr_create(pao%matrix_N_inv_diag, &
     250              :                         name="PAO matrix_N_inv_diag", &
     251              :                         dist=pao%diag_distribution, &
     252           98 :                         template=matrix_s(1)%matrix)
     253           98 :       CALL dbcsr_reserve_diag_blocks(pao%matrix_N_inv_diag)
     254           98 :       CALL dbcsr_complete_redistribute(pao%matrix_N_inv, pao%matrix_N_inv_diag)
     255              : 
     256           98 :       CALL timestop(handle)
     257           98 :    END SUBROUTINE pao_build_orthogonalizer
     258              : 
     259              : ! **************************************************************************************************
     260              : !> \brief Build rectangular matrix to converert between primary and PAO basis.
     261              : !> \param pao ...
     262              : !> \param qs_env ...
     263              : ! **************************************************************************************************
     264           98 :    SUBROUTINE pao_build_selector(pao, qs_env)
     265              :       TYPE(pao_env_type), POINTER                        :: pao
     266              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     267              : 
     268              :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_build_selector'
     269              : 
     270              :       INTEGER                                            :: acol, arow, handle, i, iatom, ikind, M, &
     271              :                                                             natoms
     272           98 :       INTEGER, DIMENSION(:), POINTER                     :: blk_sizes_aux, blk_sizes_pri
     273           98 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block_Y
     274              :       TYPE(dbcsr_iterator_type)                          :: iter
     275           98 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     276           98 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     277           98 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     278              : 
     279           98 :       CALL timeset(routineN, handle)
     280              : 
     281              :       CALL get_qs_env(qs_env, &
     282              :                       natom=natoms, &
     283              :                       matrix_s=matrix_s, &
     284              :                       qs_kind_set=qs_kind_set, &
     285           98 :                       particle_set=particle_set)
     286              : 
     287           98 :       CALL dbcsr_get_info(matrix_s(1)%matrix, col_blk_size=blk_sizes_pri)
     288              : 
     289          294 :       ALLOCATE (blk_sizes_aux(natoms))
     290          336 :       DO iatom = 1, natoms
     291          238 :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     292          238 :          CALL get_qs_kind(qs_kind_set(ikind), pao_basis_size=M)
     293          238 :          CPASSERT(M > 0)
     294          238 :          IF (blk_sizes_pri(iatom) < M) THEN
     295            0 :             CPABORT("PAO basis size exceeds primary basis size.")
     296              :          END IF
     297          574 :          blk_sizes_aux(iatom) = M
     298              :       END DO
     299              : 
     300              :       CALL dbcsr_create(pao%matrix_Y, &
     301              :                         template=matrix_s(1)%matrix, &
     302              :                         matrix_type="N", &
     303              :                         row_blk_size=blk_sizes_pri, &
     304              :                         col_blk_size=blk_sizes_aux, &
     305           98 :                         name="PAO matrix_Y")
     306           98 :       DEALLOCATE (blk_sizes_aux)
     307              : 
     308           98 :       CALL dbcsr_reserve_diag_blocks(pao%matrix_Y)
     309              : 
     310              : !$OMP PARALLEL DEFAULT(NONE) SHARED(pao) &
     311           98 : !$OMP PRIVATE(iter,arow,acol,block_Y,i,M)
     312              :       CALL dbcsr_iterator_start(iter, pao%matrix_Y)
     313              :       DO WHILE (dbcsr_iterator_blocks_left(iter))
     314              :          CALL dbcsr_iterator_next_block(iter, arow, acol, block_Y)
     315              :          M = SIZE(block_Y, 2) ! size of pao basis
     316              :          block_Y = 0.0_dp
     317              :          DO i = 1, M
     318              :             block_Y(i, i) = 1.0_dp
     319              :          END DO
     320              :       END DO
     321              :       CALL dbcsr_iterator_stop(iter)
     322              : !$OMP END PARALLEL
     323              : 
     324           98 :       CALL timestop(handle)
     325           98 :    END SUBROUTINE pao_build_selector
     326              : 
     327              : ! **************************************************************************************************
     328              : !> \brief Creates new DBCSR distribution which spreads diagonal blocks evenly across ranks
     329              : !> \param pao ...
     330              : !> \param qs_env ...
     331              : ! **************************************************************************************************
     332           98 :    SUBROUTINE pao_build_diag_distribution(pao, qs_env)
     333              :       TYPE(pao_env_type), POINTER                        :: pao
     334              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     335              : 
     336              :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_build_diag_distribution'
     337              : 
     338              :       INTEGER                                            :: handle, iatom, natoms, pgrid_cols, &
     339              :                                                             pgrid_rows
     340           98 :       INTEGER, DIMENSION(:), POINTER                     :: diag_col_dist, diag_row_dist
     341              :       TYPE(dbcsr_distribution_type)                      :: main_dist
     342           98 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     343              : 
     344           98 :       CALL timeset(routineN, handle)
     345              : 
     346           98 :       CALL get_qs_env(qs_env, natom=natoms, matrix_s=matrix_s)
     347              : 
     348              :       ! get processor grid from matrix_s
     349           98 :       CALL dbcsr_get_info(matrix=matrix_s(1)%matrix, distribution=main_dist)
     350           98 :       CALL dbcsr_distribution_get(main_dist, nprows=pgrid_rows, npcols=pgrid_cols)
     351              : 
     352              :       ! create new mapping of matrix-grid to processor-grid
     353          392 :       ALLOCATE (diag_row_dist(natoms), diag_col_dist(natoms))
     354          336 :       DO iatom = 1, natoms
     355          238 :          diag_row_dist(iatom) = MOD(iatom - 1, pgrid_rows)
     356          336 :          diag_col_dist(iatom) = MOD((iatom - 1)/pgrid_rows, pgrid_cols)
     357              :       END DO
     358              : 
     359              :       ! instanciate distribution object
     360              :       CALL dbcsr_distribution_new(pao%diag_distribution, template=main_dist, &
     361           98 :                                   row_dist=diag_row_dist, col_dist=diag_col_dist)
     362              : 
     363           98 :       DEALLOCATE (diag_row_dist, diag_col_dist)
     364              : 
     365           98 :       CALL timestop(handle)
     366          196 :    END SUBROUTINE pao_build_diag_distribution
     367              : 
     368              : ! **************************************************************************************************
     369              : !> \brief Creates the matrix_X
     370              : !> \param pao ...
     371              : !> \param qs_env ...
     372              : ! **************************************************************************************************
     373           98 :    SUBROUTINE pao_build_matrix_X(pao, qs_env)
     374              :       TYPE(pao_env_type), POINTER                        :: pao
     375              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     376              : 
     377              :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_build_matrix_X'
     378              : 
     379              :       INTEGER                                            :: handle, iatom, ikind, natoms
     380           98 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_size, row_blk_size
     381           98 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     382              : 
     383           98 :       CALL timeset(routineN, handle)
     384              : 
     385              :       CALL get_qs_env(qs_env, &
     386              :                       natom=natoms, &
     387           98 :                       particle_set=particle_set)
     388              : 
     389              :       ! determine block-sizes of matrix_X
     390          392 :       ALLOCATE (row_blk_size(natoms), col_blk_size(natoms))
     391          336 :       col_blk_size = 1
     392          336 :       DO iatom = 1, natoms
     393          238 :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     394          336 :          CALL pao_param_count(pao, qs_env, ikind, nparams=row_blk_size(iatom))
     395              :       END DO
     396              : 
     397              :       ! build actual matrix_X
     398              :       CALL dbcsr_create(pao%matrix_X, &
     399              :                         name="PAO matrix_X", &
     400              :                         dist=pao%diag_distribution, &
     401              :                         matrix_type="N", &
     402              :                         row_blk_size=row_blk_size, &
     403           98 :                         col_blk_size=col_blk_size)
     404           98 :       DEALLOCATE (row_blk_size, col_blk_size)
     405              : 
     406           98 :       CALL dbcsr_reserve_diag_blocks(pao%matrix_X)
     407           98 :       CALL dbcsr_set(pao%matrix_X, 0.0_dp)
     408              : 
     409           98 :       CALL timestop(handle)
     410           98 :    END SUBROUTINE pao_build_matrix_X
     411              : 
     412              : ! **************************************************************************************************
     413              : !> \brief Creates the matrix_H0 which contains the core hamiltonian
     414              : !> \param pao ...
     415              : !> \param qs_env ...
     416              : ! **************************************************************************************************
     417           98 :    SUBROUTINE pao_build_core_hamiltonian(pao, qs_env)
     418              :       TYPE(pao_env_type), POINTER                        :: pao
     419              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     420              : 
     421           98 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     422           98 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     423           98 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     424              : 
     425              :       CALL get_qs_env(qs_env, &
     426              :                       matrix_s=matrix_s, &
     427              :                       atomic_kind_set=atomic_kind_set, &
     428           98 :                       qs_kind_set=qs_kind_set)
     429              : 
     430              :       ! allocate matrix_H0
     431              :       CALL dbcsr_create(pao%matrix_H0, &
     432              :                         name="PAO matrix_H0", &
     433              :                         dist=pao%diag_distribution, &
     434           98 :                         template=matrix_s(1)%matrix)
     435           98 :       CALL dbcsr_reserve_diag_blocks(pao%matrix_H0)
     436              : 
     437              :       ! calculate initial atomic fock matrix H0
     438              :       ! Can't use matrix_ks from ls_scf_qs_atomic_guess(), because it's not rotationally invariant.
     439              :       ! getting H0 directly from the atomic code
     440              :       CALL calculate_atomic_fock_matrix(pao%matrix_H0, &
     441              :                                         atomic_kind_set, &
     442              :                                         qs_kind_set, &
     443           98 :                                         ounit=pao%iw)
     444              : 
     445           98 :    END SUBROUTINE pao_build_core_hamiltonian
     446              : 
     447              : ! **************************************************************************************************
     448              : !> \brief Test whether the PAO optimization has reached convergence
     449              : !> \param pao ...
     450              : !> \param ls_scf_env ...
     451              : !> \param new_energy ...
     452              : !> \param is_converged ...
     453              : ! **************************************************************************************************
     454         2616 :    SUBROUTINE pao_test_convergence(pao, ls_scf_env, new_energy, is_converged)
     455              :       TYPE(pao_env_type), POINTER                        :: pao
     456              :       TYPE(ls_scf_env_type)                              :: ls_scf_env
     457              :       REAL(KIND=dp), INTENT(IN)                          :: new_energy
     458              :       LOGICAL, INTENT(OUT)                               :: is_converged
     459              : 
     460              :       REAL(KIND=dp)                                      :: energy_diff, loop_eps, now, time_diff
     461              : 
     462              :       ! calculate progress
     463         2616 :       energy_diff = new_energy - pao%energy_prev
     464         2616 :       pao%energy_prev = new_energy
     465         2616 :       now = m_walltime()
     466         2616 :       time_diff = now - pao%step_start_time
     467         2616 :       pao%step_start_time = now
     468              : 
     469              :       ! convergence criterion
     470         2616 :       loop_eps = pao%norm_G/ls_scf_env%nelectron_total
     471         2616 :       is_converged = loop_eps < pao%eps_pao
     472              : 
     473         2616 :       IF (pao%istep > 1) THEN
     474         2540 :          IF (pao%iw > 0) WRITE (pao%iw, *) "PAO| energy improvement:", energy_diff
     475              :          ! CPWARN_IF(energy_diff>0.0_dp, "PAO| energy increased")
     476              : 
     477              :          ! print one-liner
     478         2540 :          IF (pao%iw > 0) WRITE (pao%iw, '(A,I6,11X,F20.9,1X,E10.3,1X,E10.3,1X,F9.3)') &
     479         1270 :             " PAO| step ", &
     480         1270 :             pao%istep, &
     481         1270 :             new_energy, &
     482         1270 :             loop_eps, &
     483         1270 :             pao%linesearch%step_size, & !prev step, which let to the current energy
     484         2540 :             time_diff
     485              :       END IF
     486         2616 :    END SUBROUTINE pao_test_convergence
     487              : 
     488              : ! **************************************************************************************************
     489              : !> \brief Calculate the pao energy
     490              : !> \param pao ...
     491              : !> \param qs_env ...
     492              : !> \param ls_scf_env ...
     493              : !> \param energy ...
     494              : ! **************************************************************************************************
     495        11810 :    SUBROUTINE pao_calc_energy(pao, qs_env, ls_scf_env, energy)
     496              :       TYPE(pao_env_type), POINTER                        :: pao
     497              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     498              :       TYPE(ls_scf_env_type), TARGET                      :: ls_scf_env
     499              :       REAL(KIND=dp), INTENT(OUT)                         :: energy
     500              : 
     501              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_calc_energy'
     502              : 
     503              :       INTEGER                                            :: handle, ispin
     504              :       REAL(KIND=dp)                                      :: penalty, trace_PH
     505              : 
     506        11810 :       CALL timeset(routineN, handle)
     507              : 
     508              :       ! calculate matrix U, which determines the pao basis
     509        11810 :       CALL pao_calc_AB(pao, qs_env, ls_scf_env, gradient=.FALSE., penalty=penalty)
     510              : 
     511              :       ! calculat S, S_inv, S_sqrt, and S_sqrt_inv in the new pao basis
     512        11810 :       CALL pao_rebuild_S(qs_env, ls_scf_env)
     513              : 
     514              :       ! calculate the density matrix P in the pao basis
     515        11810 :       CALL pao_dm_trs4(qs_env, ls_scf_env)
     516              : 
     517              :       ! calculate the energy from the trace(PH) in the pao basis
     518        11810 :       energy = 0.0_dp
     519        23620 :       DO ispin = 1, ls_scf_env%nspins
     520        11810 :          CALL dbcsr_dot(ls_scf_env%matrix_p(ispin), ls_scf_env%matrix_ks(ispin), trace_PH)
     521        23620 :          energy = energy + trace_PH
     522              :       END DO
     523              : 
     524              :       ! add penalty term
     525        11810 :       energy = energy + penalty
     526              : 
     527        11810 :       IF (pao%iw > 0) THEN
     528         5905 :          WRITE (pao%iw, *) ""
     529         5905 :          WRITE (pao%iw, *) "PAO| energy:", energy, "penalty:", penalty
     530              :       END IF
     531        11810 :       CALL timestop(handle)
     532        11810 :    END SUBROUTINE pao_calc_energy
     533              : 
     534              : ! **************************************************************************************************
     535              : !> \brief Ensure that the number of electrons is correct.
     536              : !> \param ls_scf_env ...
     537              : ! **************************************************************************************************
     538        10330 :    SUBROUTINE pao_check_trace_PS(ls_scf_env)
     539              :       TYPE(ls_scf_env_type)                              :: ls_scf_env
     540              : 
     541              :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_check_trace_PS'
     542              : 
     543              :       INTEGER                                            :: handle, ispin
     544              :       REAL(KIND=dp)                                      :: tmp, trace_PS
     545              :       TYPE(dbcsr_type)                                   :: matrix_S_desym
     546              : 
     547        10330 :       CALL timeset(routineN, handle)
     548        10330 :       CALL dbcsr_create(matrix_S_desym, template=ls_scf_env%matrix_s, matrix_type="N")
     549        10330 :       CALL dbcsr_desymmetrize(ls_scf_env%matrix_s, matrix_S_desym)
     550              : 
     551        10330 :       trace_PS = 0.0_dp
     552        20660 :       DO ispin = 1, ls_scf_env%nspins
     553        10330 :          CALL dbcsr_dot(ls_scf_env%matrix_p(ispin), matrix_S_desym, tmp)
     554        20660 :          trace_PS = trace_PS + tmp
     555              :       END DO
     556              : 
     557        10330 :       CALL dbcsr_release(matrix_S_desym)
     558              : 
     559        10330 :       IF (ABS(ls_scf_env%nelectron_total - trace_PS) > 0.5) THEN
     560            0 :          CPABORT("Number of electrons wrong. Trace(PS) ="//cp_to_string(trace_PS))
     561              :       END IF
     562              : 
     563        10330 :       CALL timestop(handle)
     564        10330 :    END SUBROUTINE pao_check_trace_PS
     565              : 
     566              : ! **************************************************************************************************
     567              : !> \brief Read primary density matrix from file.
     568              : !> \param pao ...
     569              : !> \param qs_env ...
     570              : ! **************************************************************************************************
     571           56 :    SUBROUTINE pao_read_preopt_dm(pao, qs_env)
     572              :       TYPE(pao_env_type), POINTER                        :: pao
     573              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     574              : 
     575              :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_read_preopt_dm'
     576              : 
     577              :       INTEGER                                            :: handle, ispin
     578              :       REAL(KIND=dp)                                      :: cs_pos
     579              :       TYPE(dbcsr_distribution_type)                      :: dist
     580           28 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s, rho_ao
     581              :       TYPE(dbcsr_type)                                   :: matrix_tmp
     582              :       TYPE(dft_control_type), POINTER                    :: dft_control
     583              :       TYPE(qs_energy_type), POINTER                      :: energy
     584              :       TYPE(qs_rho_type), POINTER                         :: rho
     585              : 
     586           28 :       CALL timeset(routineN, handle)
     587              : 
     588              :       CALL get_qs_env(qs_env, &
     589              :                       dft_control=dft_control, &
     590              :                       matrix_s=matrix_s, &
     591              :                       rho=rho, &
     592           28 :                       energy=energy)
     593              : 
     594           28 :       CALL qs_rho_get(rho, rho_ao=rho_ao)
     595              : 
     596           28 :       IF (dft_control%nspins /= 1) CPABORT("open shell not yet implemented")
     597              : 
     598           28 :       CALL dbcsr_get_info(matrix_s(1)%matrix, distribution=dist)
     599              : 
     600           56 :       DO ispin = 1, dft_control%nspins
     601           28 :          CALL dbcsr_binary_read(pao%preopt_dm_file, matrix_new=matrix_tmp, distribution=dist)
     602           28 :          cs_pos = dbcsr_checksum(matrix_tmp, pos=.TRUE.)
     603           28 :          IF (pao%iw > 0) WRITE (pao%iw, *) "PAO| Read restart DM "// &
     604           14 :             TRIM(pao%preopt_dm_file)//" with checksum: ", cs_pos
     605           28 :          CALL dbcsr_copy(rho_ao(ispin)%matrix, matrix_tmp, keep_sparsity=.TRUE.)
     606           56 :          CALL dbcsr_release(matrix_tmp)
     607              :       END DO
     608              : 
     609              :       ! calculate corresponding ks matrix
     610           28 :       CALL qs_rho_update_rho(rho, qs_env=qs_env)
     611           28 :       CALL qs_ks_did_change(qs_env%ks_env, rho_changed=.TRUE.)
     612              :       CALL qs_ks_update_qs_env(qs_env, calculate_forces=.FALSE., &
     613           28 :                                just_energy=.FALSE., print_active=.TRUE.)
     614           28 :       IF (pao%iw > 0) WRITE (pao%iw, *) "PAO| Quickstep energy from restart density:", energy%total
     615              : 
     616           28 :       CALL timestop(handle)
     617              : 
     618           28 :    END SUBROUTINE pao_read_preopt_dm
     619              : 
     620              : ! **************************************************************************************************
     621              : !> \brief Rebuilds S, S_inv, S_sqrt, and S_sqrt_inv in the pao basis
     622              : !> \param qs_env ...
     623              : !> \param ls_scf_env ...
     624              : ! **************************************************************************************************
     625        11810 :    SUBROUTINE pao_rebuild_S(qs_env, ls_scf_env)
     626              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     627              :       TYPE(ls_scf_env_type), TARGET                      :: ls_scf_env
     628              : 
     629              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_rebuild_S'
     630              : 
     631              :       INTEGER                                            :: handle
     632        11810 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     633              : 
     634        11810 :       CALL timeset(routineN, handle)
     635              : 
     636        11810 :       CALL dbcsr_release(ls_scf_env%matrix_s_inv)
     637        11810 :       CALL dbcsr_release(ls_scf_env%matrix_s_sqrt)
     638        11810 :       CALL dbcsr_release(ls_scf_env%matrix_s_sqrt_inv)
     639              : 
     640        11810 :       CALL get_qs_env(qs_env, matrix_s=matrix_s)
     641        11810 :       CALL ls_scf_init_matrix_s(matrix_s(1)%matrix, ls_scf_env)
     642              : 
     643        11810 :       CALL timestop(handle)
     644        11810 :    END SUBROUTINE pao_rebuild_S
     645              : 
     646              : ! **************************************************************************************************
     647              : !> \brief Calculate density matrix using TRS4 purification
     648              : !> \param qs_env ...
     649              : !> \param ls_scf_env ...
     650              : ! **************************************************************************************************
     651        11810 :    SUBROUTINE pao_dm_trs4(qs_env, ls_scf_env)
     652              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     653              :       TYPE(ls_scf_env_type), TARGET                      :: ls_scf_env
     654              : 
     655              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_dm_trs4'
     656              : 
     657              :       CHARACTER(LEN=default_path_length)                 :: project_name
     658              :       INTEGER                                            :: handle, ispin, nelectron_spin_real, nspin
     659              :       LOGICAL                                            :: converged
     660              :       REAL(KIND=dp)                                      :: homo_spin, lumo_spin, mu_spin
     661              :       TYPE(cp_logger_type), POINTER                      :: logger
     662        11810 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_ks
     663              : 
     664        11810 :       CALL timeset(routineN, handle)
     665        11810 :       logger => cp_get_default_logger()
     666              :       project_name = logger%iter_info%project_name
     667        11810 :       nspin = ls_scf_env%nspins
     668              : 
     669        11810 :       CALL get_qs_env(qs_env, matrix_ks=matrix_ks)
     670        23620 :       DO ispin = 1, nspin
     671              :          CALL matrix_qs_to_ls(ls_scf_env%matrix_ks(ispin), matrix_ks(ispin)%matrix, &
     672        11810 :                               ls_scf_env%ls_mstruct, covariant=.TRUE.)
     673              : 
     674        11810 :          nelectron_spin_real = ls_scf_env%nelectron_spin(ispin)
     675        11810 :          IF (ls_scf_env%nspins == 1) nelectron_spin_real = nelectron_spin_real/2
     676              :          CALL density_matrix_trs4(ls_scf_env%matrix_p(ispin), ls_scf_env%matrix_ks(ispin), &
     677              :                                   ls_scf_env%matrix_s_sqrt_inv, &
     678              :                                   nelectron_spin_real, ls_scf_env%eps_filter, homo_spin, lumo_spin, mu_spin, &
     679              :                                   dynamic_threshold=.FALSE., converged=converged, &
     680              :                                   max_iter_lanczos=ls_scf_env%max_iter_lanczos, &
     681        11810 :                                   eps_lanczos=ls_scf_env%eps_lanczos)
     682        23620 :          IF (.NOT. converged) CPABORT("TRS4 did not converge")
     683              :       END DO
     684              : 
     685        11810 :       IF (nspin == 1) CALL dbcsr_scale(ls_scf_env%matrix_p(1), 2.0_dp)
     686              : 
     687        11810 :       CALL timestop(handle)
     688        11810 :    END SUBROUTINE pao_dm_trs4
     689              : 
     690              : ! **************************************************************************************************
     691              : !> \brief Debugging routine for checking the analytic gradient.
     692              : !> \param pao ...
     693              : !> \param qs_env ...
     694              : !> \param ls_scf_env ...
     695              : ! **************************************************************************************************
     696         2628 :    SUBROUTINE pao_check_grad(pao, qs_env, ls_scf_env)
     697              :       TYPE(pao_env_type), POINTER                        :: pao
     698              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     699              :       TYPE(ls_scf_env_type), TARGET                      :: ls_scf_env
     700              : 
     701              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_check_grad'
     702              : 
     703              :       INTEGER                                            :: handle, i, iatom, j, natoms
     704         2616 :       INTEGER, DIMENSION(:), POINTER                     :: blk_sizes_col, blk_sizes_row
     705              :       LOGICAL                                            :: found
     706              :       REAL(dp)                                           :: delta, delta_max, eps, Gij_num
     707         2616 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block_G, block_X
     708              :       TYPE(ls_mstruct_type), POINTER                     :: ls_mstruct
     709              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     710              : 
     711         2604 :       IF (pao%check_grad_tol < 0.0_dp) RETURN ! no checking
     712              : 
     713           12 :       CALL timeset(routineN, handle)
     714              : 
     715           12 :       ls_mstruct => ls_scf_env%ls_mstruct
     716              : 
     717           12 :       CALL get_qs_env(qs_env, para_env=para_env, natom=natoms)
     718              : 
     719           12 :       eps = pao%num_grad_eps
     720           12 :       delta_max = 0.0_dp
     721              : 
     722           12 :       CALL dbcsr_get_info(pao%matrix_X, col_blk_size=blk_sizes_col, row_blk_size=blk_sizes_row)
     723              : 
     724              :       ! can not use an iterator here, because other DBCSR routines are called within loop.
     725           38 :       DO iatom = 1, natoms
     726           26 :          IF (pao%iw > 0) WRITE (pao%iw, *) 'PAO| checking gradient of atom ', iatom
     727           26 :          CALL dbcsr_get_block_p(matrix=pao%matrix_X, row=iatom, col=iatom, block=block_X, found=found)
     728              : 
     729           26 :          IF (ASSOCIATED(block_X)) THEN !only one node actually has the block
     730           13 :             CALL dbcsr_get_block_p(matrix=pao%matrix_G, row=iatom, col=iatom, block=block_G, found=found)
     731           13 :             CPASSERT(ASSOCIATED(block_G))
     732              :          END IF
     733              : 
     734          586 :          DO i = 1, blk_sizes_row(iatom)
     735         1070 :             DO j = 1, blk_sizes_col(iatom)
     736          828 :                SELECT CASE (pao%num_grad_order)
     737              :                CASE (2) ! calculate derivative to 2th order
     738          306 :                   Gij_num = -eval_point(block_X, i, j, -eps, pao, ls_scf_env, qs_env)
     739          306 :                   Gij_num = Gij_num + eval_point(block_X, i, j, +eps, pao, ls_scf_env, qs_env)
     740          306 :                   Gij_num = Gij_num/(2.0_dp*eps)
     741              : 
     742              :                CASE (4) ! calculate derivative to 4th order
     743          180 :                   Gij_num = eval_point(block_X, i, j, -2_dp*eps, pao, ls_scf_env, qs_env)
     744          180 :                   Gij_num = Gij_num - 8_dp*eval_point(block_X, i, j, -1_dp*eps, pao, ls_scf_env, qs_env)
     745          180 :                   Gij_num = Gij_num + 8_dp*eval_point(block_X, i, j, +1_dp*eps, pao, ls_scf_env, qs_env)
     746          180 :                   Gij_num = Gij_num - eval_point(block_X, i, j, +2_dp*eps, pao, ls_scf_env, qs_env)
     747          180 :                   Gij_num = Gij_num/(12.0_dp*eps)
     748              : 
     749              :                CASE (6) ! calculate derivative to 6th order
     750           36 :                   Gij_num = -1_dp*eval_point(block_X, i, j, -3_dp*eps, pao, ls_scf_env, qs_env)
     751           36 :                   Gij_num = Gij_num + 9_dp*eval_point(block_X, i, j, -2_dp*eps, pao, ls_scf_env, qs_env)
     752           36 :                   Gij_num = Gij_num - 45_dp*eval_point(block_X, i, j, -1_dp*eps, pao, ls_scf_env, qs_env)
     753           36 :                   Gij_num = Gij_num + 45_dp*eval_point(block_X, i, j, +1_dp*eps, pao, ls_scf_env, qs_env)
     754           36 :                   Gij_num = Gij_num - 9_dp*eval_point(block_X, i, j, +2_dp*eps, pao, ls_scf_env, qs_env)
     755           36 :                   Gij_num = Gij_num + 1_dp*eval_point(block_X, i, j, +3_dp*eps, pao, ls_scf_env, qs_env)
     756           36 :                   Gij_num = Gij_num/(60.0_dp*eps)
     757              : 
     758              :                CASE DEFAULT
     759          522 :                   CPABORT("Unsupported numerical derivative order: "//cp_to_string(pao%num_grad_order))
     760              :                END SELECT
     761              : 
     762         1044 :                IF (ASSOCIATED(block_X)) THEN
     763          261 :                   delta = ABS(Gij_num - block_G(i, j))
     764          261 :                   delta_max = MAX(delta_max, delta)
     765              :                   !WRITE (*,*) "gradient check", iatom, i, j, Gij_num, block_G(i,j), delta
     766              :                END IF
     767              :             END DO
     768              :          END DO
     769              :       END DO
     770              : 
     771           12 :       CALL para_env%max(delta_max)
     772           12 :       IF (pao%iw > 0) WRITE (pao%iw, *) 'PAO| checked gradient, max delta:', delta_max
     773           12 :       IF (delta_max > pao%check_grad_tol) CALL cp_abort(__LOCATION__, &
     774            0 :                                                         "Analytic and numeric gradients differ too much:"//cp_to_string(delta_max))
     775              : 
     776           12 :       CALL timestop(handle)
     777         2616 :    END SUBROUTINE pao_check_grad
     778              : 
     779              : ! **************************************************************************************************
     780              : !> \brief Helper routine for pao_check_grad()
     781              : !> \param block_X ...
     782              : !> \param i ...
     783              : !> \param j ...
     784              : !> \param eps ...
     785              : !> \param pao ...
     786              : !> \param ls_scf_env ...
     787              : !> \param qs_env ...
     788              : !> \return ...
     789              : ! **************************************************************************************************
     790         3096 :    FUNCTION eval_point(block_X, i, j, eps, pao, ls_scf_env, qs_env) RESULT(energy)
     791              :       REAL(dp), DIMENSION(:, :), POINTER                 :: block_X
     792              :       INTEGER, INTENT(IN)                                :: i, j
     793              :       REAL(dp), INTENT(IN)                               :: eps
     794              :       TYPE(pao_env_type), POINTER                        :: pao
     795              :       TYPE(ls_scf_env_type), TARGET                      :: ls_scf_env
     796              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     797              :       REAL(dp)                                           :: energy
     798              : 
     799              :       REAL(dp)                                           :: old_Xij
     800              : 
     801         1548 :       IF (ASSOCIATED(block_X)) THEN
     802          774 :          old_Xij = block_X(i, j) ! backup old block_X
     803          774 :          block_X(i, j) = block_X(i, j) + eps ! add perturbation
     804              :       END IF
     805              : 
     806              :       ! calculate energy
     807         1548 :       CALL pao_calc_energy(pao, qs_env, ls_scf_env, energy)
     808              : 
     809              :       ! restore old block_X
     810         1548 :       IF (ASSOCIATED(block_X)) THEN
     811          774 :          block_X(i, j) = old_Xij
     812              :       END IF
     813              : 
     814         1548 :    END FUNCTION eval_point
     815              : 
     816              : ! **************************************************************************************************
     817              : !> \brief Stores density matrix as initial guess for next SCF optimization.
     818              : !> \param qs_env ...
     819              : !> \param ls_scf_env ...
     820              : ! **************************************************************************************************
     821          588 :    SUBROUTINE pao_store_P(qs_env, ls_scf_env)
     822              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     823              :       TYPE(ls_scf_env_type), TARGET                      :: ls_scf_env
     824              : 
     825              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_store_P'
     826              : 
     827              :       INTEGER                                            :: handle, ispin, istore
     828          294 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     829              :       TYPE(dft_control_type), POINTER                    :: dft_control
     830              :       TYPE(ls_mstruct_type), POINTER                     :: ls_mstruct
     831              :       TYPE(pao_env_type), POINTER                        :: pao
     832              : 
     833            0 :       IF (ls_scf_env%scf_history%nstore == 0) RETURN
     834          294 :       CALL timeset(routineN, handle)
     835          294 :       ls_mstruct => ls_scf_env%ls_mstruct
     836          294 :       pao => ls_scf_env%pao_env
     837          294 :       CALL get_qs_env(qs_env, dft_control=dft_control, matrix_s=matrix_s)
     838              : 
     839          294 :       ls_scf_env%scf_history%istore = ls_scf_env%scf_history%istore + 1
     840          294 :       istore = MOD(ls_scf_env%scf_history%istore - 1, ls_scf_env%scf_history%nstore) + 1
     841          294 :       IF (pao%iw > 0) WRITE (pao%iw, *) "PAO| Storing density matrix for ASPC guess in slot:", istore
     842              : 
     843              :       ! initialize storage
     844          294 :       IF (ls_scf_env%scf_history%istore <= ls_scf_env%scf_history%nstore) THEN
     845          216 :          DO ispin = 1, dft_control%nspins
     846          216 :             CALL dbcsr_create(ls_scf_env%scf_history%matrix(ispin, istore), template=matrix_s(1)%matrix)
     847              :          END DO
     848              :       END IF
     849              : 
     850              :       ! We are storing the density matrix in the non-orthonormal primary basis.
     851              :       ! While the orthonormal basis would yield better extrapolations,
     852              :       ! we simply can not afford to calculat S_sqrt in the primary basis.
     853          588 :       DO ispin = 1, dft_control%nspins
     854              :          ! transform into primary basis
     855              :          CALL matrix_ls_to_qs(ls_scf_env%scf_history%matrix(ispin, istore), ls_scf_env%matrix_p(ispin), &
     856          588 :                               ls_scf_env%ls_mstruct, covariant=.FALSE., keep_sparsity=.FALSE.)
     857              :       END DO
     858              : 
     859          294 :       CALL timestop(handle)
     860          294 :    END SUBROUTINE pao_store_P
     861              : 
     862              : ! **************************************************************************************************
     863              : !> \brief Provide an initial guess for the density matrix
     864              : !> \param pao ...
     865              : !> \param qs_env ...
     866              : !> \param ls_scf_env ...
     867              : ! **************************************************************************************************
     868          294 :    SUBROUTINE pao_guess_initial_P(pao, qs_env, ls_scf_env)
     869              :       TYPE(pao_env_type), POINTER                        :: pao
     870              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     871              :       TYPE(ls_scf_env_type), TARGET                      :: ls_scf_env
     872              : 
     873              :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_guess_initial_P'
     874              : 
     875              :       INTEGER                                            :: handle
     876              : 
     877          294 :       CALL timeset(routineN, handle)
     878              : 
     879          294 :       IF (ls_scf_env%scf_history%istore > 0) THEN
     880          196 :          CALL pao_aspc_guess_P(pao, qs_env, ls_scf_env)
     881          196 :          pao%need_initial_scf = .TRUE.
     882              :       ELSE
     883           98 :          IF (LEN_TRIM(pao%preopt_dm_file) > 0) THEN
     884           28 :             CALL pao_read_preopt_dm(pao, qs_env)
     885           28 :             pao%need_initial_scf = .FALSE.
     886           28 :             pao%preopt_dm_file = "" ! load only for first MD step
     887              :          ELSE
     888           70 :             CALL ls_scf_qs_atomic_guess(qs_env, ls_scf_env, ls_scf_env%energy_init)
     889           70 :             IF (pao%iw > 0) WRITE (pao%iw, '(A,F20.9)') &
     890           35 :                " PAO| Energy from initial atomic guess:", ls_scf_env%energy_init
     891           70 :             pao%need_initial_scf = .TRUE.
     892              :          END IF
     893              :       END IF
     894              : 
     895          294 :       CALL timestop(handle)
     896              : 
     897          294 :    END SUBROUTINE pao_guess_initial_P
     898              : 
     899              : ! **************************************************************************************************
     900              : !> \brief Run the Always Stable Predictor-Corrector to guess an initial density matrix
     901              : !> \param pao ...
     902              : !> \param qs_env ...
     903              : !> \param ls_scf_env ...
     904              : ! **************************************************************************************************
     905          196 :    SUBROUTINE pao_aspc_guess_P(pao, qs_env, ls_scf_env)
     906              :       TYPE(pao_env_type), POINTER                        :: pao
     907              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     908              :       TYPE(ls_scf_env_type), TARGET                      :: ls_scf_env
     909              : 
     910              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_aspc_guess_P'
     911              : 
     912              :       INTEGER                                            :: handle, iaspc, ispin, istore, naspc
     913              :       REAL(dp)                                           :: alpha
     914          196 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     915              :       TYPE(dbcsr_type)                                   :: matrix_P
     916              :       TYPE(dft_control_type), POINTER                    :: dft_control
     917              :       TYPE(ls_mstruct_type), POINTER                     :: ls_mstruct
     918              : 
     919          196 :       CALL timeset(routineN, handle)
     920          196 :       ls_mstruct => ls_scf_env%ls_mstruct
     921          196 :       CPASSERT(ls_scf_env%scf_history%istore > 0)
     922          196 :       CALL cite_reference(Kolafa2004)
     923          196 :       CALL cite_reference(Kuhne2007)
     924          196 :       CALL get_qs_env(qs_env, dft_control=dft_control, matrix_s=matrix_s)
     925              : 
     926          196 :       IF (pao%iw > 0) WRITE (pao%iw, *) "PAO| Calculating initial guess with ASPC"
     927              : 
     928          196 :       CALL dbcsr_create(matrix_P, template=matrix_s(1)%matrix)
     929              : 
     930          196 :       naspc = MIN(ls_scf_env%scf_history%istore, ls_scf_env%scf_history%nstore)
     931          392 :       DO ispin = 1, dft_control%nspins
     932              :          ! actual extrapolation
     933          196 :          CALL dbcsr_set(matrix_P, 0.0_dp)
     934          416 :          DO iaspc = 1, naspc
     935              :             alpha = (-1.0_dp)**(iaspc + 1)*REAL(iaspc, KIND=dp)* &
     936          220 :                     binomial(2*naspc, naspc - iaspc)/binomial(2*naspc - 2, naspc - 1)
     937          220 :             istore = MOD(ls_scf_env%scf_history%istore - iaspc, ls_scf_env%scf_history%nstore) + 1
     938          416 :             CALL dbcsr_add(matrix_P, ls_scf_env%scf_history%matrix(ispin, istore), 1.0_dp, alpha)
     939              :          END DO
     940              : 
     941              :          ! transform back from primary basis into pao basis
     942          392 :          CALL matrix_qs_to_ls(ls_scf_env%matrix_p(ispin), matrix_P, ls_scf_env%ls_mstruct, covariant=.FALSE.)
     943              :       END DO
     944              : 
     945          196 :       CALL dbcsr_release(matrix_P)
     946              : 
     947              :       ! linear combination of P's is not idempotent. A bit of McWeeny is needed to ensure it is again
     948          392 :       DO ispin = 1, dft_control%nspins
     949          196 :          IF (dft_control%nspins == 1) CALL dbcsr_scale(ls_scf_env%matrix_p(ispin), 0.5_dp)
     950              :          ! to ensure that noisy blocks do not build up during MD (in particular with curvy) filter that guess a bit more
     951          196 :          CALL dbcsr_filter(ls_scf_env%matrix_p(ispin), ls_scf_env%eps_filter**(2.0_dp/3.0_dp))
     952              :          ! we could go to the orthonomal basis, but it seems not worth the trouble
     953              :          ! TODO : 10 iterations is a conservative upper bound, figure out when it fails
     954          196 :          CALL purify_mcweeny(ls_scf_env%matrix_p(ispin:ispin), ls_scf_env%matrix_s, ls_scf_env%eps_filter, 10)
     955          392 :          IF (dft_control%nspins == 1) CALL dbcsr_scale(ls_scf_env%matrix_p(ispin), 2.0_dp)
     956              :       END DO
     957              : 
     958          196 :       CALL pao_check_trace_PS(ls_scf_env) ! sanity check
     959              : 
     960              :       ! compute corresponding energy and ks matrix
     961          196 :       CALL ls_scf_dm_to_ks(qs_env, ls_scf_env, ls_scf_env%energy_init, iscf=0)
     962              : 
     963          196 :       CALL timestop(handle)
     964          196 :    END SUBROUTINE pao_aspc_guess_P
     965              : 
     966              : ! **************************************************************************************************
     967              : !> \brief Calculate the forces contributed by PAO
     968              : !> \param qs_env ...
     969              : !> \param ls_scf_env ...
     970              : ! **************************************************************************************************
     971           44 :    SUBROUTINE pao_add_forces(qs_env, ls_scf_env)
     972              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     973              :       TYPE(ls_scf_env_type), TARGET                      :: ls_scf_env
     974              : 
     975              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_add_forces'
     976              : 
     977              :       INTEGER                                            :: handle, iatom, natoms
     978           44 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: forces
     979              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     980              :       TYPE(pao_env_type), POINTER                        :: pao
     981           44 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     982              : 
     983           44 :       CALL timeset(routineN, handle)
     984           44 :       pao => ls_scf_env%pao_env
     985              : 
     986           44 :       IF (pao%iw > 0) WRITE (pao%iw, *) "PAO| Adding forces."
     987              : 
     988           44 :       IF (pao%max_pao /= 0) THEN
     989           20 :          IF (pao%penalty_strength /= 0.0_dp) THEN
     990            0 :             CPABORT("PAO forces require PENALTY_STRENGTH or MAX_PAO set to zero")
     991              :          END IF
     992           20 :          IF (pao%linpot_regu_strength /= 0.0_dp) THEN
     993            0 :             CPABORT("PAO forces require LINPOT_REGULARIZATION_STRENGTH or MAX_PAO set to zero")
     994              :          END IF
     995           20 :          IF (pao%regularization /= 0.0_dp) THEN
     996            0 :             CPABORT("PAO forces require REGULARIZATION or MAX_PAO set to zero")
     997              :          END IF
     998              :       END IF
     999              : 
    1000              :       CALL get_qs_env(qs_env, &
    1001              :                       para_env=para_env, &
    1002              :                       particle_set=particle_set, &
    1003           44 :                       natom=natoms)
    1004              : 
    1005          132 :       ALLOCATE (forces(natoms, 3))
    1006           44 :       CALL pao_calc_AB(pao, qs_env, ls_scf_env, gradient=.TRUE., forces=forces) ! without penalty terms
    1007              : 
    1008           44 :       IF (SIZE(pao%ml_training_set) > 0) THEN
    1009           18 :          CALL pao_ml_forces(pao, qs_env, pao%matrix_G, forces)
    1010              :       END IF
    1011              : 
    1012           44 :       IF (ALLOCATED(pao%models)) THEN
    1013            2 :          CALL pao_model_forces(pao, qs_env, pao%matrix_G, forces)
    1014              :       END IF
    1015              : 
    1016           44 :       CALL para_env%sum(forces)
    1017          150 :       DO iatom = 1, natoms
    1018          468 :          particle_set(iatom)%f = particle_set(iatom)%f + forces(iatom, :)
    1019              :       END DO
    1020              : 
    1021           44 :       DEALLOCATE (forces)
    1022              : 
    1023           44 :       CALL timestop(handle)
    1024              : 
    1025           44 :    END SUBROUTINE pao_add_forces
    1026              : 
    1027              : END MODULE pao_methods
        

Generated by: LCOV version 2.0-1