LCOV - code coverage report
Current view: top level - src/xc - xc_gauxc_functional.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:21ef868) Lines: 74.8 % 519 388
Test Date: 2026-08-14 07:04:57 Functions: 86.7 % 15 13

            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              : #ifdef __GAUXC
       9              : #include "gauxc/gauxc_config.f"
      10              : #endif
      11              : 
      12              : MODULE xc_gauxc_functional
      13              :    USE atomic_kind_types,               ONLY: atomic_kind_type,&
      14              :                                               get_atomic_kind
      15              :    USE cell_types,                      ONLY: cell_type
      16              :    USE cp_control_types,                ONLY: dft_control_type
      17              :    USE cp_dbcsr_api,                    ONLY: dbcsr_add,&
      18              :                                               dbcsr_create,&
      19              :                                               dbcsr_finalize,&
      20              :                                               dbcsr_get_info,&
      21              :                                               dbcsr_p_type,&
      22              :                                               dbcsr_release
      23              :    USE cp_dbcsr_operations,             ONLY: dbcsr_allocate_matrix_set,&
      24              :                                               dbcsr_deallocate_matrix_set
      25              :    USE cp_log_handling,                 ONLY: cp_logger_get_default_io_unit
      26              :    USE external_potential_types,        ONLY: gth_potential_type,&
      27              :                                               sgp_potential_type
      28              :    USE input_constants,                 ONLY: xc_vdw_fun_nonloc
      29              :    USE input_section_types,             ONLY: section_vals_get_subs_vals,&
      30              :                                               section_vals_get_subs_vals2,&
      31              :                                               section_vals_type,&
      32              :                                               section_vals_val_get
      33              :    USE iso_c_binding,                   ONLY: c_char,&
      34              :                                               c_double,&
      35              :                                               c_int,&
      36              :                                               c_null_char
      37              :    USE kinds,                           ONLY: default_path_length,&
      38              :                                               default_string_length,&
      39              :                                               dp
      40              :    USE message_passing,                 ONLY: mp_comm_self,&
      41              :                                               mp_para_env_type
      42              :    USE particle_types,                  ONLY: particle_type
      43              :    USE qs_energy_types,                 ONLY: qs_energy_type
      44              :    USE qs_environment_types,            ONLY: get_qs_env,&
      45              :                                               qs_environment_type
      46              :    USE qs_force_types,                  ONLY: qs_force_type
      47              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      48              :                                               has_nlcc,&
      49              :                                               qs_kind_type
      50              :    USE qs_ks_types,                     ONLY: qs_ks_env_type,&
      51              :                                               set_ks_env
      52              :    USE qs_rho_types,                    ONLY: qs_rho_get,&
      53              :                                               qs_rho_type
      54              :    USE qs_scf_types,                    ONLY: qs_scf_env_type
      55              :    USE string_utilities,                ONLY: uppercase
      56              :    USE xc_gauxc_cache,                  ONLY: cp_gauxc_cache_params,&
      57              :                                               cp_gauxc_cache_type,&
      58              :                                               gauxc_cache_init
      59              :    USE xc_gauxc_interface,              ONLY: &
      60              :         cp_gauxc_basisset_type, cp_gauxc_grid_type, cp_gauxc_integrator_type, &
      61              :         cp_gauxc_molecule_type, cp_gauxc_status_type, cp_gauxc_xc_gradient_type, cp_gauxc_xc_type, &
      62              :         gauxc_check_status, gauxc_compute_xc, gauxc_compute_xc_gradient, gauxc_create_basisset, &
      63              :         gauxc_create_grid, gauxc_create_integrator, gauxc_create_molecule, gauxc_destroy_basisset, &
      64              :         gauxc_destroy_grid, gauxc_destroy_integrator, gauxc_destroy_molecule, &
      65              :         gauxc_write_basisset_hdf5, gauxc_write_molecule_hdf5
      66              :    USE xc_input_constants,              ONLY: skala_gapw_paw_one_center
      67              :    USE xc_rho_cflags_types,             ONLY: xc_rho_cflags_type
      68              : #include "../base/base_uses.f90"
      69              : 
      70              :    IMPLICIT NONE
      71              : 
      72              :    PRIVATE
      73              : 
      74              :    LOGICAL, PARAMETER :: debug_this_module = .TRUE.
      75              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'xc_gauxc_functional'
      76              : 
      77              :    PUBLIC :: apply_gauxc, gauxc_gapw_has_paw_pseudopotentials, skala_info, &
      78              :              xc_section_uses_gauxc
      79              : 
      80              :    INTERFACE
      81              :       INTEGER(c_int) FUNCTION c_setenv(name, value, overwrite) BIND(C, name="setenv")
      82              :          IMPORT :: c_char, c_int
      83              :          CHARACTER(KIND=c_char), DIMENSION(*), INTENT(IN) :: name, value
      84              :          INTEGER(c_int), VALUE                            :: overwrite
      85              :       END FUNCTION c_setenv
      86              : 
      87              :       INTEGER(c_int) FUNCTION c_unsetenv(name) BIND(C, name="unsetenv")
      88              :          IMPORT :: c_char, c_int
      89              :          CHARACTER(KIND=c_char), DIMENSION(*), INTENT(IN) :: name
      90              :       END FUNCTION c_unsetenv
      91              :    END INTERFACE
      92              : 
      93              : CONTAINS
      94              : 
      95              : ! **************************************************************************************************
      96              : !> \brief Set the GauXC Skala atom chunk environment knob when the CP2K keyword is explicit.
      97              : !> \param atom_chunk_size ...
      98              : !> \param is_explicit ...
      99              : ! **************************************************************************************************
     100           28 :    SUBROUTINE set_gauxc_model_atom_chunk_env(atom_chunk_size, is_explicit)
     101              :       INTEGER, INTENT(IN)                                :: atom_chunk_size
     102              :       LOGICAL, INTENT(IN)                                :: is_explicit
     103              : 
     104              :       CHARACTER(LEN=32)                                  :: chunk_value
     105              :       INTEGER(c_int)                                     :: ierr
     106              : 
     107           28 :       IF (.NOT. is_explicit) RETURN
     108              : 
     109            2 :       IF (atom_chunk_size < 0) THEN
     110            0 :          ierr = c_unsetenv("GAUXC_ONEDFT_ATOM_CHUNK_SIZE"//c_null_char)
     111              :       ELSE
     112            2 :          WRITE (chunk_value, '(I0)') atom_chunk_size
     113              :          ierr = c_setenv( &
     114              :                 "GAUXC_ONEDFT_ATOM_CHUNK_SIZE"//c_null_char, &
     115              :                 TRIM(chunk_value)//c_null_char, &
     116            2 :                 1_c_int)
     117              :       END IF
     118            2 :       IF (ierr /= 0_c_int) THEN
     119              :          CALL cp_abort(__LOCATION__, &
     120            0 :                        "Could not set GAUXC_ONEDFT_ATOM_CHUNK_SIZE for GauXC Skala.")
     121              :       END IF
     122              :    END SUBROUTINE set_gauxc_model_atom_chunk_env
     123              : 
     124              : ! **************************************************************************************************
     125              : !> \brief ...
     126              : !> \param dbcsr_mat ...
     127              : !> \param dense_mat ...
     128              : !> \param para_env ...
     129              : ! **************************************************************************************************
     130          500 :    SUBROUTINE dbcsr_to_dense(dbcsr_mat, dense_mat, para_env)
     131              :       USE cp_dbcsr_api, ONLY: dbcsr_distribution_get, dbcsr_distribution_type, dbcsr_get_info, &
     132              :                               dbcsr_get_matrix_type, dbcsr_get_readonly_block_p, &
     133              :                               dbcsr_get_stored_coordinates, dbcsr_type_antisymmetric, &
     134              :                               dbcsr_type_symmetric
     135              :       TYPE(dbcsr_p_type), INTENT(IN)                     :: dbcsr_mat
     136              :       REAL(c_double), ALLOCATABLE, DIMENSION(:, :), &
     137              :          INTENT(INOUT)                                   :: dense_mat
     138              :       TYPE(mp_para_env_type), INTENT(IN), POINTER        :: para_env
     139              : 
     140              :       CHARACTER                                          :: matrix_type
     141              :       INTEGER :: col, col_end, col_start, icol, irow, mynode, nblkcols_total, nblkrows_total, &
     142              :          ncols, nrows, numnodes, owner, row, row_end, row_start
     143          500 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: c_offset, r_offset
     144          500 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_size, row_blk_size
     145              :       LOGICAL                                            :: found
     146          500 :       REAL(c_double), POINTER                            :: block(:, :)
     147              :       TYPE(dbcsr_distribution_type)                      :: dist
     148              : 
     149              :       CALL dbcsr_get_info(dbcsr_mat%matrix, &
     150              :                           row_blk_size=row_blk_size, &
     151              :                           col_blk_size=col_blk_size, &
     152              :                           nblkrows_total=nblkrows_total, &
     153              :                           nblkcols_total=nblkcols_total, &
     154              :                           nfullrows_total=nrows, &
     155              :                           nfullcols_total=ncols, &
     156          500 :                           distribution=dist)
     157          500 :       CALL dbcsr_distribution_get(dist, mynode=mynode, numnodes=numnodes)
     158          500 :       matrix_type = dbcsr_get_matrix_type(dbcsr_mat%matrix)
     159              : 
     160          500 :       IF (.NOT. ALLOCATED(dense_mat)) THEN
     161         2000 :          ALLOCATE (dense_mat(nrows, ncols))
     162            0 :       ELSE IF (.NOT. ALL(SHAPE(dense_mat) == [nrows, ncols])) THEN
     163            0 :          DEALLOCATE (dense_mat)
     164            0 :          ALLOCATE (dense_mat(nrows, ncols))
     165              :       ELSE
     166            0 :          CPASSERT(ALL(SHAPE(dense_mat) == [nrows, ncols]))
     167              :       END IF
     168          500 :       dense_mat = 0._dp
     169              : 
     170         2500 :       ALLOCATE (r_offset(nblkrows_total), c_offset(nblkcols_total))
     171              : 
     172          500 :       r_offset(1) = 1
     173         1216 :       DO row = 2, nblkrows_total
     174         1216 :          r_offset(row) = r_offset(row - 1) + row_blk_size(row - 1)
     175              :       END DO
     176          500 :       c_offset(1) = 1
     177         1216 :       DO col = 2, nblkcols_total
     178         1216 :          c_offset(col) = c_offset(col - 1) + col_blk_size(col - 1)
     179              :       END DO
     180              : 
     181              :       ! Replicated DBCSR blocks must enter the following MPI sum exactly once.
     182         1716 :       DO irow = 1, nblkrows_total
     183         5108 :          DO icol = 1, nblkcols_total
     184         3392 :             IF (numnodes == 1 .AND. para_env%num_pe > 1 .AND. para_env%mepos /= 0) CYCLE
     185         3392 :             CALL dbcsr_get_stored_coordinates(dbcsr_mat%matrix, irow, icol, owner)
     186         3392 :             IF (owner /= mynode) CYCLE
     187              :             CALL dbcsr_get_readonly_block_p(matrix=dbcsr_mat%matrix, row=irow, col=icol, &
     188         1840 :                                             block=block, found=found)
     189         1840 :             IF (.NOT. found) CYCLE
     190         1260 :             row_start = r_offset(irow)
     191         1260 :             row_end = row_start + row_blk_size(irow) - 1
     192         1260 :             col_start = c_offset(icol)
     193         1260 :             col_end = col_start + col_blk_size(icol) - 1
     194        60722 :             dense_mat(row_start:row_end, col_start:col_end) = block
     195         7708 :             IF (irow /= icol) THEN
     196          580 :                IF (matrix_type == dbcsr_type_symmetric) THEN
     197        25797 :                   dense_mat(col_start:col_end, row_start:row_end) = TRANSPOSE(block)
     198            0 :                ELSE IF (matrix_type == dbcsr_type_antisymmetric) THEN
     199            0 :                   dense_mat(col_start:col_end, row_start:row_end) = -TRANSPOSE(block)
     200              :                END IF
     201              :             END IF
     202              :          END DO
     203              :       END DO
     204              : 
     205          500 :       DEALLOCATE (r_offset, c_offset)
     206              : 
     207          500 :    END SUBROUTINE dbcsr_to_dense
     208              : 
     209              : ! ******, ***********************************************************************************
     210              : !> \brief Convert a dense symmetric matrix to a DBCSR matrix with full upper block structure.
     211              : !>        This creates all upper-triangular blocks, not just those present in a template.
     212              : !>        This is needed because GauXC computes VXC for the full dense density matrix.
     213              : !> \param dense_mat Input dense matrix
     214              : !> \param template_dbcsr Template DBCSR matrix for distribution and block sizes
     215              : !> \return dbcsr_mat Output DBCSR matrix with full upper block structure
     216              : ! **************************************************************************************************
     217         1044 :    FUNCTION dense_to_dbcsr(dense_mat, template_dbcsr) RESULT(dbcsr_mat)
     218              :       USE cp_dbcsr_api, ONLY: &
     219              :          dbcsr_create, &
     220              :          dbcsr_distribution_get, &
     221              :          dbcsr_distribution_type, &
     222              :          dbcsr_finalize, &
     223              :          dbcsr_get_info, &
     224              :          dbcsr_get_stored_coordinates, &
     225              :          dbcsr_init_p, &
     226              :          dbcsr_put_block, &
     227              :          dbcsr_release, &
     228              :          dbcsr_type_symmetric, &
     229              :          dbcsr_work_create
     230              :       REAL(c_double), DIMENSION(:, :), INTENT(IN)        :: dense_mat
     231              :       TYPE(dbcsr_p_type), INTENT(IN)                     :: template_dbcsr
     232              :       TYPE(dbcsr_p_type)                                 :: dbcsr_mat
     233              : 
     234              :       INTEGER                                            :: col, icol, irow, mynode, nblkcols_total, &
     235              :                                                             nblkrows_total, ncols, nrows, owner, &
     236              :                                                             row
     237          522 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: c_offset, r_offset
     238          522 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_size, row_blk_size
     239              :       TYPE(dbcsr_distribution_type)                      :: dist
     240              : 
     241              :       CALL dbcsr_get_info(template_dbcsr%matrix, &
     242              :                           row_blk_size=row_blk_size, &
     243              :                           col_blk_size=col_blk_size, &
     244              :                           nblkrows_total=nblkrows_total, &
     245              :                           nblkcols_total=nblkcols_total, &
     246              :                           nfullrows_total=nrows, &
     247              :                           nfullcols_total=ncols, &
     248          522 :                           distribution=dist)
     249          522 :       CALL dbcsr_distribution_get(dist, mynode=mynode)
     250              : 
     251          522 :       CPASSERT(nrows == SIZE(dense_mat, 1))
     252          522 :       CPASSERT(ncols == SIZE(dense_mat, 2))
     253              : 
     254          522 :       CALL dbcsr_init_p(dbcsr_mat%matrix)
     255              :       CALL dbcsr_create(dbcsr_mat%matrix, &
     256              :                         template=template_dbcsr%matrix, &
     257              :                         name="VXC from GauXC (dense)", &
     258          522 :                         matrix_type=dbcsr_type_symmetric)
     259          522 :       CALL dbcsr_work_create(dbcsr_mat%matrix, work_mutable=.TRUE.)
     260              : 
     261         2610 :       ALLOCATE (r_offset(nblkrows_total), c_offset(nblkcols_total))
     262              : 
     263          522 :       r_offset(1) = 1
     264         1260 :       DO row = 2, nblkrows_total
     265         1260 :          r_offset(row) = r_offset(row - 1) + row_blk_size(row - 1)
     266              :       END DO
     267          522 :       c_offset(1) = 1
     268         1260 :       DO col = 2, nblkcols_total
     269         1260 :          c_offset(col) = c_offset(col - 1) + col_blk_size(col - 1)
     270              :       END DO
     271              : 
     272         1782 :       DO irow = 1, nblkrows_total
     273         5262 :          DO icol = 1, nblkcols_total
     274         3480 :             IF (irow > icol) CYCLE
     275         2370 :             CALL dbcsr_get_stored_coordinates(dbcsr_mat%matrix, irow, icol, owner)
     276         2370 :             IF (owner /= mynode) CYCLE
     277              :             CALL dbcsr_put_block(dbcsr_mat%matrix, irow, icol, &
     278              :                                  0.5_dp*( &
     279              :                                  dense_mat(r_offset(irow):r_offset(irow) + row_blk_size(irow) - 1, &
     280              :                                            c_offset(icol):c_offset(icol) + col_blk_size(icol) - 1) + &
     281              :                                  TRANSPOSE(dense_mat(r_offset(icol):r_offset(icol) + row_blk_size(icol) - 1, &
     282        67685 :                                                      c_offset(irow):c_offset(irow) + col_blk_size(irow) - 1))))
     283              :          END DO
     284              :       END DO
     285              : 
     286          522 :       CALL dbcsr_finalize(dbcsr_mat%matrix)
     287              : 
     288          522 :       DEALLOCATE (r_offset, c_offset)
     289              : 
     290          522 :    END FUNCTION dense_to_dbcsr
     291              : 
     292              : ! **************************************************************************************************
     293              : !> \brief ...
     294              : !> \param xc_section ...
     295              : !> \return ...
     296              : ! **************************************************************************************************
     297          478 :    FUNCTION get_gauxc_functional(xc_section) RESULT(gauxc_functional_section)
     298              :       TYPE(section_vals_type), INTENT(in), POINTER       :: xc_section
     299              :       TYPE(section_vals_type), POINTER                   :: gauxc_functional_section
     300              : 
     301              :       INTEGER                                            :: ifun
     302              :       TYPE(section_vals_type), POINTER                   :: functionals, xc_fun
     303              : 
     304          478 :       NULLIFY (gauxc_functional_section)
     305              : 
     306          478 :       functionals => section_vals_get_subs_vals(xc_section, "XC_FUNCTIONAL")
     307          478 :       IF (.NOT. ASSOCIATED(functionals)) THEN
     308            0 :          CPABORT("XC_FUNCTIONAL section not found")
     309              :       END IF
     310              : 
     311          478 :       ifun = 0
     312              :       DO
     313          956 :          ifun = ifun + 1
     314          956 :          xc_fun => section_vals_get_subs_vals2(functionals, i_section=ifun)
     315          956 :          IF (.NOT. ASSOCIATED(xc_fun)) EXIT
     316          478 :          IF (xc_fun%section%name /= "GAUXC" .OR. ifun > 1) THEN
     317            0 :             CPABORT("GauXC functionals are mutually exclusive with any other functional.")
     318              :          END IF
     319          478 :          gauxc_functional_section => xc_fun
     320              :       END DO
     321              : 
     322          478 :       IF (.NOT. ASSOCIATED(gauxc_functional_section)) THEN
     323            0 :          CPABORT("No XC functional found in XC_FUNCTIONAL section")
     324              :       END IF
     325          478 :    END FUNCTION get_gauxc_functional
     326              : 
     327              : ! **************************************************************************************************
     328              : !> \brief ...
     329              : !> \param xc_section ...
     330              : !> \return ...
     331              : ! **************************************************************************************************
     332        14344 :    FUNCTION xc_section_uses_gauxc(xc_section) RESULT(uses_gauxc)
     333              :       TYPE(section_vals_type), INTENT(in), POINTER       :: xc_section
     334              :       LOGICAL                                            :: uses_gauxc
     335              : 
     336              :       INTEGER                                            :: ifun
     337              :       TYPE(section_vals_type), POINTER                   :: functionals, xc_fun
     338              : 
     339        14344 :       uses_gauxc = .FALSE.
     340        14344 :       IF (.NOT. ASSOCIATED(xc_section)) RETURN
     341              : 
     342        14344 :       functionals => section_vals_get_subs_vals(xc_section, "XC_FUNCTIONAL")
     343        14344 :       IF (.NOT. ASSOCIATED(functionals)) RETURN
     344              : 
     345        14344 :       ifun = 0
     346              :       DO
     347        28014 :          ifun = ifun + 1
     348        28014 :          xc_fun => section_vals_get_subs_vals2(functionals, i_section=ifun)
     349        28014 :          IF (.NOT. ASSOCIATED(xc_fun)) EXIT
     350        28014 :          IF (xc_fun%section%name == "GAUXC") THEN
     351              :             uses_gauxc = .TRUE.
     352              :             EXIT
     353              :          END IF
     354              :       END DO
     355              : 
     356              :    END FUNCTION xc_section_uses_gauxc
     357              : 
     358              : ! **************************************************************************************************
     359              : !> \brief Return whether GauXC GAPW mode sees pseudopotential kinds.
     360              : !> \param qs_kind_set ...
     361              : !> \return ...
     362              : ! **************************************************************************************************
     363          110 :    FUNCTION gauxc_gapw_has_pseudopotentials(qs_kind_set) RESULT(has_pseudopotentials)
     364              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     365              :       LOGICAL                                            :: has_pseudopotentials
     366              : 
     367              :       INTEGER                                            :: ikind
     368              :       TYPE(gth_potential_type), POINTER                  :: gth_potential
     369              :       TYPE(sgp_potential_type), POINTER                  :: sgp_potential
     370              : 
     371          110 :       CPASSERT(ASSOCIATED(qs_kind_set))
     372              : 
     373          110 :       has_pseudopotentials = .FALSE.
     374          116 :       DO ikind = 1, SIZE(qs_kind_set)
     375          110 :          NULLIFY (gth_potential, sgp_potential)
     376              :          CALL get_qs_kind(qs_kind_set(ikind), &
     377              :                           gth_potential=gth_potential, &
     378          110 :                           sgp_potential=sgp_potential)
     379          116 :          IF (ASSOCIATED(gth_potential) .OR. ASSOCIATED(sgp_potential)) THEN
     380              :             has_pseudopotentials = .TRUE.
     381              :             EXIT
     382              :          END IF
     383              :       END DO
     384              : 
     385          110 :    END FUNCTION gauxc_gapw_has_pseudopotentials
     386              : 
     387              : ! **************************************************************************************************
     388              : !> \brief Return whether GauXC GAPW mode sees pseudopotential one-center GAPW kinds.
     389              : !> \param qs_kind_set ...
     390              : !> \return ...
     391              : ! **************************************************************************************************
     392          372 :    FUNCTION gauxc_gapw_has_paw_pseudopotentials(qs_kind_set) RESULT(has_paw_pseudopotentials)
     393              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     394              :       LOGICAL                                            :: has_paw_pseudopotentials
     395              : 
     396              :       INTEGER                                            :: ikind
     397              :       LOGICAL                                            :: paw_atom
     398              :       TYPE(gth_potential_type), POINTER                  :: gth_potential
     399              :       TYPE(sgp_potential_type), POINTER                  :: sgp_potential
     400              : 
     401          372 :       CPASSERT(ASSOCIATED(qs_kind_set))
     402              : 
     403          372 :       has_paw_pseudopotentials = .FALSE.
     404          790 :       DO ikind = 1, SIZE(qs_kind_set)
     405          570 :          NULLIFY (gth_potential, sgp_potential)
     406              :          CALL get_qs_kind(qs_kind_set(ikind), &
     407              :                           gth_potential=gth_potential, &
     408              :                           paw_atom=paw_atom, &
     409          570 :                           sgp_potential=sgp_potential)
     410          570 :          IF ((ASSOCIATED(gth_potential) .OR. ASSOCIATED(sgp_potential)) .AND. &
     411          790 :              paw_atom) THEN
     412              :             has_paw_pseudopotentials = .TRUE.
     413              :             EXIT
     414              :          END IF
     415              :       END DO
     416              : 
     417          372 :    END FUNCTION gauxc_gapw_has_paw_pseudopotentials
     418              : 
     419              : ! **************************************************************************************************
     420              : !> \brief Check the current periodic scope of the CP2K-GauXC bridge
     421              : !> \param dft_control ...
     422              : !> \param cell ...
     423              : !> \param qs_kind_set ...
     424              : !> \param do_kpoints ...
     425              : !> \param periodic_reference ...
     426              : !> \note This path keeps isolated validation cells usable under PERIODIC XYZ.
     427              : !>       It intentionally does not implement compact periodic GauXC quadrature.
     428              : ! **************************************************************************************************
     429          478 :    SUBROUTINE ensure_gauxc_periodic_reference_scope( &
     430              :       dft_control, cell, qs_kind_set, do_kpoints, periodic_reference)
     431              :       TYPE(dft_control_type), POINTER                    :: dft_control
     432              :       TYPE(cell_type), POINTER                           :: cell
     433              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     434              :       LOGICAL, INTENT(IN)                                :: do_kpoints, periodic_reference
     435              : 
     436              :       INTEGER                                            :: ikind
     437              :       LOGICAL                                            :: is_periodic
     438              :       TYPE(gth_potential_type), POINTER                  :: gth_potential
     439              :       TYPE(sgp_potential_type), POINTER                  :: sgp_potential
     440              : 
     441          478 :       CPASSERT(ASSOCIATED(dft_control))
     442          478 :       CPASSERT(ASSOCIATED(qs_kind_set))
     443              : 
     444          478 :       is_periodic = .FALSE.
     445          814 :       IF (ASSOCIATED(cell)) is_periodic = ANY(cell%perd /= 0)
     446              : 
     447          478 :       IF (do_kpoints) THEN
     448              :          CALL cp_abort(__LOCATION__, &
     449              :                        "GauXC currently supports only Gamma-only density matrices in CP2K. "// &
     450            0 :                        "Periodic k-point density matrices require a dedicated GauXC periodic interface.")
     451              :       END IF
     452          478 :       IF (dft_control%nimages /= 1) THEN
     453              :          CALL cp_abort(__LOCATION__, &
     454              :                        "GauXC currently supports only a single AO image in CP2K. "// &
     455            0 :                        "Periodic neighbour-cell AO blocks require a dedicated GauXC periodic interface.")
     456              :       END IF
     457          478 :       IF (.NOT. is_periodic) RETURN
     458              : 
     459          366 :       IF (.NOT. periodic_reference) THEN
     460              :          CALL cp_abort(__LOCATION__, &
     461              :                        "Periodic GauXC calculations in CP2K require GAUXC%PERIODIC_REFERENCE T. "// &
     462              :                        "This opt-in documents that the current path is only an isolated-cell, "// &
     463              :                        "Gamma-only, single-image METHOD GPW reference path using GauXC molecular "// &
     464            0 :                        "quadrature, not a dedicated periodic GauXC interface.")
     465              :       END IF
     466              : 
     467         1464 :       IF (.NOT. ALL(cell%perd == 1)) THEN
     468              :          CALL cp_abort(__LOCATION__, &
     469              :                        "The current GauXC isolated-cell reference path supports only PERIODIC XYZ. "// &
     470            0 :                        "Partial periodicity requires a dedicated GauXC periodic interface.")
     471              :       END IF
     472          366 :       IF (.NOT. dft_control%qs_control%gpw) THEN
     473              :          CALL cp_abort(__LOCATION__, &
     474              :                        "The current GauXC isolated-cell reference path is limited to METHOD GPW with GTH "// &
     475            0 :                        "pseudopotentials. GAPW, GAPW_XC, and other QS methods are not supported here.")
     476              :       END IF
     477              : 
     478          846 :       DO ikind = 1, SIZE(qs_kind_set)
     479          480 :          NULLIFY (gth_potential, sgp_potential)
     480              :          CALL get_qs_kind(qs_kind_set(ikind), &
     481              :                           gth_potential=gth_potential, &
     482          480 :                           sgp_potential=sgp_potential)
     483          846 :          IF (.NOT. ASSOCIATED(gth_potential) .OR. ASSOCIATED(sgp_potential)) THEN
     484              :             CALL cp_abort(__LOCATION__, &
     485              :                           "The current GauXC isolated-cell reference path is limited to GTH pseudopotentials. "// &
     486            0 :                           "Use non-periodic all-electron GAPW validation for molecular GAPW cases.")
     487              :          END IF
     488              :       END DO
     489              : 
     490              :    END SUBROUTINE ensure_gauxc_periodic_reference_scope
     491              : 
     492              : ! **************************************************************************************************
     493              : !> \brief adds a replicated GauXC energy gradient to the local CP2K force accumulator
     494              : !> \param exc_grad ...
     495              : !> \param force ...
     496              : !> \param atomic_kind_set ...
     497              : !> \param para_env ...
     498              : ! **************************************************************************************************
     499            6 :    SUBROUTINE add_gauxc_gradient_to_force(exc_grad, force, atomic_kind_set, para_env)
     500              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: exc_grad
     501              :       TYPE(qs_force_type), DIMENSION(:), POINTER         :: force
     502              :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     503              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     504              : 
     505              :       INTEGER                                            :: ia, iatom, ikind, natom_kind
     506              :       TYPE(atomic_kind_type), POINTER                    :: atomic_kind
     507              : 
     508            6 :       CPASSERT(ASSOCIATED(force))
     509            6 :       CPASSERT(ASSOCIATED(atomic_kind_set))
     510              : 
     511            6 :       IF (para_env%mepos /= 0) RETURN
     512              : 
     513            8 :       DO ikind = 1, SIZE(atomic_kind_set, 1)
     514            5 :          atomic_kind => atomic_kind_set(ikind)
     515            5 :          CALL get_atomic_kind(atomic_kind=atomic_kind, natom=natom_kind)
     516           16 :          DO ia = 1, natom_kind
     517            8 :             iatom = atomic_kind%atom_list(ia)
     518              :             force(ikind)%rho_elec(:, ia) = force(ikind)%rho_elec(:, ia) + &
     519           37 :                                            exc_grad(3*iatom - 2:3*iatom)
     520              :          END DO
     521              :       END DO
     522              : 
     523              :    END SUBROUTINE add_gauxc_gradient_to_force
     524              : 
     525              : ! **************************************************************************************************
     526              : !> \brief compute a GauXC XC energy for diagnostic finite differences
     527              : !> \param particle_set_eval ...
     528              : !> \param qs_kind_set ...
     529              : !> \param density_scalar ...
     530              : !> \param nspins ...
     531              : !> \param model_name ...
     532              : !> \param xc_fun_name ...
     533              : !> \param grid_type ...
     534              : !> \param radial_quadrature ...
     535              : !> \param pruning_scheme ...
     536              : !> \param lb_exec_space ...
     537              : !> \param int_exec_space ...
     538              : !> \param lwd_kernel ...
     539              : !> \param batch_size ...
     540              : !> \param device_runtime_fill_fraction ...
     541              : !> \param exc ...
     542              : !> \param density_zeta ...
     543              : ! **************************************************************************************************
     544           12 :    SUBROUTINE gauxc_xc_energy_for_particles( &
     545           12 :       particle_set_eval, qs_kind_set, density_scalar, nspins, model_name, &
     546              :       xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     547           12 :       int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, exc, density_zeta)
     548              :       TYPE(particle_type), DIMENSION(:), INTENT(IN)      :: particle_set_eval
     549              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     550              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: density_scalar
     551              :       INTEGER, INTENT(IN)                                :: nspins
     552              :       CHARACTER(len=*), INTENT(IN) :: model_name, xc_fun_name, grid_type, radial_quadrature, &
     553              :          pruning_scheme, lb_exec_space, int_exec_space, lwd_kernel
     554              :       INTEGER, INTENT(IN)                                :: batch_size
     555              :       REAL(KIND=dp), INTENT(IN)                          :: device_runtime_fill_fraction
     556              :       REAL(KIND=dp), INTENT(OUT)                         :: exc
     557              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
     558              :          OPTIONAL                                        :: density_zeta
     559              : 
     560              :       TYPE(cp_gauxc_basisset_type)                       :: gauxc_basis_fd
     561              :       TYPE(cp_gauxc_grid_type)                           :: gauxc_grid_fd
     562              :       TYPE(cp_gauxc_integrator_type)                     :: gauxc_integrator_fd
     563              :       TYPE(cp_gauxc_molecule_type)                       :: gauxc_mol_fd
     564              :       TYPE(cp_gauxc_status_type)                         :: gauxc_status
     565           12 :       TYPE(cp_gauxc_xc_type)                             :: gauxc_xc_result
     566              : 
     567           12 :       gauxc_mol_fd = gauxc_create_molecule(particle_set_eval, gauxc_status)
     568           12 :       CALL gauxc_check_status(gauxc_status)
     569           12 :       gauxc_basis_fd = gauxc_create_basisset(qs_kind_set, particle_set_eval, gauxc_status)
     570           12 :       CALL gauxc_check_status(gauxc_status)
     571              :       gauxc_grid_fd = gauxc_create_grid( &
     572              :                       gauxc_mol_fd, &
     573              :                       gauxc_basis_fd, &
     574              :                       grid_type, &
     575              :                       radial_quadrature, &
     576              :                       pruning_scheme, &
     577              :                       lb_exec_space, &
     578              :                       batch_size, &
     579              :                       device_runtime_fill_fraction, &
     580              :                       gauxc_status, &
     581              :                       mpi_comm=mp_comm_self%get_handle(), &
     582           12 :                       force_new_runtime=.TRUE.)
     583           12 :       CALL gauxc_check_status(gauxc_status)
     584              :       gauxc_integrator_fd = gauxc_create_integrator( &
     585              :                             TRIM(xc_fun_name), &
     586              :                             gauxc_grid_fd, &
     587              :                             int_exec_space, &
     588              :                             lwd_kernel, &
     589              :                             nspins, &
     590           12 :                             gauxc_status)
     591           12 :       CALL gauxc_check_status(gauxc_status)
     592              : 
     593           12 :       IF (nspins == 1) THEN
     594              :          gauxc_xc_result = gauxc_compute_xc( &
     595              :                            gauxc_integrator_fd, &
     596              :                            density_scalar, &
     597              :                            nspins=nspins, &
     598              :                            status=gauxc_status, &
     599           12 :                            model=TRIM(model_name))
     600              :       ELSE
     601            0 :          CPASSERT(nspins == 2)
     602            0 :          CPASSERT(PRESENT(density_zeta))
     603              :          gauxc_xc_result = gauxc_compute_xc( &
     604              :                            gauxc_integrator_fd, &
     605              :                            density_scalar, &
     606              :                            density_zeta, &
     607              :                            nspins, &
     608              :                            gauxc_status, &
     609            0 :                            model=TRIM(model_name))
     610              :       END IF
     611           12 :       CALL gauxc_check_status(gauxc_status)
     612           12 :       exc = gauxc_xc_result%exc
     613              : 
     614           12 :       IF (ALLOCATED(gauxc_xc_result%vxc_scalar)) DEALLOCATE (gauxc_xc_result%vxc_scalar)
     615           12 :       IF (ALLOCATED(gauxc_xc_result%vxc_zeta)) DEALLOCATE (gauxc_xc_result%vxc_zeta)
     616              : 
     617           12 :       CALL gauxc_destroy_integrator(gauxc_integrator_fd, gauxc_status)
     618           12 :       CALL gauxc_check_status(gauxc_status)
     619           12 :       CALL gauxc_destroy_grid(gauxc_grid_fd, gauxc_status)
     620           12 :       CALL gauxc_check_status(gauxc_status)
     621           12 :       CALL gauxc_destroy_basisset(gauxc_basis_fd, gauxc_status)
     622           12 :       CALL gauxc_check_status(gauxc_status)
     623           12 :       CALL gauxc_destroy_molecule(gauxc_mol_fd, gauxc_status)
     624           12 :       CALL gauxc_check_status(gauxc_status)
     625              : 
     626           24 :    END SUBROUTINE gauxc_xc_energy_for_particles
     627              : 
     628              : ! **************************************************************************************************
     629              : !> \brief compute a finite-difference GauXC XC nuclear gradient at fixed density
     630              : !> \param particle_set ...
     631              : !> \param qs_kind_set ...
     632              : !> \param density_scalar ...
     633              : !> \param nspins ...
     634              : !> \param model_name ...
     635              : !> \param xc_fun_name ...
     636              : !> \param grid_type ...
     637              : !> \param radial_quadrature ...
     638              : !> \param pruning_scheme ...
     639              : !> \param lb_exec_space ...
     640              : !> \param int_exec_space ...
     641              : !> \param lwd_kernel ...
     642              : !> \param batch_size ...
     643              : !> \param device_runtime_fill_fraction ...
     644              : !> \param dx ...
     645              : !> \param para_env ...
     646              : !> \param exc_grad ...
     647              : !> \param density_zeta ...
     648              : ! **************************************************************************************************
     649            2 :    SUBROUTINE gauxc_xc_gradient_fd( &
     650            2 :       particle_set, qs_kind_set, density_scalar, nspins, model_name, &
     651              :       xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     652              :       int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, dx, para_env, exc_grad, &
     653            2 :       density_zeta)
     654              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     655              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     656              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: density_scalar
     657              :       INTEGER, INTENT(IN)                                :: nspins
     658              :       CHARACTER(len=*), INTENT(IN) :: model_name, xc_fun_name, grid_type, radial_quadrature, &
     659              :          pruning_scheme, lb_exec_space, int_exec_space, lwd_kernel
     660              :       INTEGER, INTENT(IN)                                :: batch_size
     661              :       REAL(KIND=dp), INTENT(IN)                          :: device_runtime_fill_fraction, dx
     662              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     663              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:), &
     664              :          INTENT(OUT)                                     :: exc_grad
     665              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
     666              :          OPTIONAL                                        :: density_zeta
     667              : 
     668              :       INTEGER                                            :: iatom, idir
     669              :       REAL(KIND=dp)                                      :: xc_minus, xc_plus
     670            2 :       TYPE(particle_type), ALLOCATABLE, DIMENSION(:)     :: particle_set_minus, particle_set_plus
     671              : 
     672            2 :       CPASSERT(ASSOCIATED(particle_set))
     673            2 :       CPASSERT(dx > 0.0_dp)
     674              : 
     675            6 :       ALLOCATE (exc_grad(3*SIZE(particle_set)))
     676            2 :       exc_grad = 0.0_dp
     677              : 
     678            2 :       IF (para_env%mepos == 0) THEN
     679           30 :          ALLOCATE (particle_set_minus(SIZE(particle_set)), particle_set_plus(SIZE(particle_set)))
     680              : 
     681            3 :          DO iatom = 1, SIZE(particle_set)
     682            9 :             DO idir = 1, 3
     683           24 :                particle_set_minus = particle_set
     684           24 :                particle_set_plus = particle_set
     685            6 :                particle_set_minus(iatom)%r(idir) = particle_set_minus(iatom)%r(idir) - dx
     686            6 :                particle_set_plus(iatom)%r(idir) = particle_set_plus(iatom)%r(idir) + dx
     687            6 :                IF (PRESENT(density_zeta)) THEN
     688              :                   CALL gauxc_xc_energy_for_particles( &
     689              :                      particle_set_plus, qs_kind_set, density_scalar, nspins, model_name, &
     690              :                      xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     691              :                      int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, xc_plus, &
     692            0 :                      density_zeta=density_zeta)
     693              :                   CALL gauxc_xc_energy_for_particles( &
     694              :                      particle_set_minus, qs_kind_set, density_scalar, nspins, model_name, &
     695              :                      xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     696              :                      int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, xc_minus, &
     697            0 :                      density_zeta=density_zeta)
     698              :                ELSE
     699              :                   CALL gauxc_xc_energy_for_particles( &
     700              :                      particle_set_plus, qs_kind_set, density_scalar, nspins, model_name, &
     701              :                      xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     702            6 :                      int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, xc_plus)
     703              :                   CALL gauxc_xc_energy_for_particles( &
     704              :                      particle_set_minus, qs_kind_set, density_scalar, nspins, model_name, &
     705              :                      xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     706            6 :                      int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, xc_minus)
     707              :                END IF
     708            8 :                exc_grad(3*iatom - 3 + idir) = (xc_plus - xc_minus)/(2.0_dp*dx)
     709              :             END DO
     710              :          END DO
     711              : 
     712            1 :          DEALLOCATE (particle_set_minus, particle_set_plus)
     713              :       END IF
     714              : 
     715            2 :       CALL para_env%bcast(exc_grad, 0)
     716              : 
     717            2 :    END SUBROUTINE gauxc_xc_gradient_fd
     718              : 
     719              : ! **************************************************************************************************
     720              : !> \brief finite-difference check of the molecular GauXC XC virial diagnostic
     721              : !> \param exc_grad ...
     722              : !> \param particle_set ...
     723              : !> \param qs_kind_set ...
     724              : !> \param density_scalar ...
     725              : !> \param nspins ...
     726              : !> \param model_name ...
     727              : !> \param xc_fun_name ...
     728              : !> \param grid_type ...
     729              : !> \param radial_quadrature ...
     730              : !> \param pruning_scheme ...
     731              : !> \param lb_exec_space ...
     732              : !> \param int_exec_space ...
     733              : !> \param lwd_kernel ...
     734              : !> \param batch_size ...
     735              : !> \param device_runtime_fill_fraction ...
     736              : !> \param dx ...
     737              : !> \param para_env ...
     738              : !> \param density_zeta ...
     739              : ! **************************************************************************************************
     740            0 :    SUBROUTINE debug_gauxc_molecular_virial( &
     741            0 :       exc_grad, particle_set, qs_kind_set, density_scalar, nspins, model_name, &
     742              :       xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     743            0 :       int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, dx, para_env, density_zeta)
     744              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: exc_grad
     745              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     746              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     747              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: density_scalar
     748              :       INTEGER, INTENT(IN)                                :: nspins
     749              :       CHARACTER(len=*), INTENT(IN) :: model_name, xc_fun_name, grid_type, radial_quadrature, &
     750              :          pruning_scheme, lb_exec_space, int_exec_space, lwd_kernel
     751              :       INTEGER, INTENT(IN)                                :: batch_size
     752              :       REAL(KIND=dp), INTENT(IN)                          :: device_runtime_fill_fraction, dx
     753              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     754              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
     755              :          OPTIONAL                                        :: density_zeta
     756              : 
     757              :       INTEGER                                            :: iatom, iw
     758              :       REAL(KIND=dp)                                      :: analytic_trace, diff_trace, &
     759              :                                                             numerical_trace, xc_minus, xc_plus
     760              :       REAL(KIND=dp), DIMENSION(3)                        :: center, displacement, grad
     761            0 :       TYPE(particle_type), ALLOCATABLE, DIMENSION(:)     :: particle_set_minus, particle_set_plus
     762              : 
     763            0 :       CPASSERT(ASSOCIATED(particle_set))
     764            0 :       CPASSERT(SIZE(exc_grad) == 3*SIZE(particle_set))
     765              : 
     766            0 :       IF (para_env%mepos /= 0) RETURN
     767              : 
     768            0 :       center = 0.0_dp
     769            0 :       DO iatom = 1, SIZE(particle_set)
     770            0 :          center = center + particle_set(iatom)%r
     771              :       END DO
     772            0 :       center = center/REAL(SIZE(particle_set), dp)
     773              : 
     774            0 :       ALLOCATE (particle_set_minus(SIZE(particle_set)), particle_set_plus(SIZE(particle_set)))
     775            0 :       particle_set_minus = particle_set
     776            0 :       particle_set_plus = particle_set
     777              : 
     778            0 :       analytic_trace = 0.0_dp
     779            0 :       DO iatom = 1, SIZE(particle_set)
     780            0 :          grad = exc_grad(3*iatom - 2:3*iatom)
     781            0 :          displacement = particle_set(iatom)%r - center
     782            0 :          analytic_trace = analytic_trace + DOT_PRODUCT(grad, displacement)
     783            0 :          particle_set_minus(iatom)%r = center + (1.0_dp - dx)*displacement
     784            0 :          particle_set_plus(iatom)%r = center + (1.0_dp + dx)*displacement
     785              :       END DO
     786            0 :       analytic_trace = analytic_trace/3.0_dp
     787              : 
     788            0 :       IF (PRESENT(density_zeta)) THEN
     789              :          CALL gauxc_xc_energy_for_particles( &
     790              :             particle_set_plus, qs_kind_set, density_scalar, nspins, model_name, &
     791              :             xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     792              :             int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, xc_plus, &
     793            0 :             density_zeta=density_zeta)
     794              :          CALL gauxc_xc_energy_for_particles( &
     795              :             particle_set_minus, qs_kind_set, density_scalar, nspins, model_name, &
     796              :             xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     797              :             int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, xc_minus, &
     798            0 :             density_zeta=density_zeta)
     799              :       ELSE
     800              :          CALL gauxc_xc_energy_for_particles( &
     801              :             particle_set_plus, qs_kind_set, density_scalar, nspins, model_name, &
     802              :             xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     803            0 :             int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, xc_plus)
     804              :          CALL gauxc_xc_energy_for_particles( &
     805              :             particle_set_minus, qs_kind_set, density_scalar, nspins, model_name, &
     806              :             xc_fun_name, grid_type, radial_quadrature, pruning_scheme, lb_exec_space, &
     807            0 :             int_exec_space, lwd_kernel, batch_size, device_runtime_fill_fraction, xc_minus)
     808              :       END IF
     809              : 
     810            0 :       numerical_trace = (xc_plus - xc_minus)/(2.0_dp*dx)/3.0_dp
     811            0 :       diff_trace = analytic_trace - numerical_trace
     812              : 
     813            0 :       iw = cp_logger_get_default_io_unit()
     814            0 :       IF (iw > 0) THEN
     815              :          WRITE (UNIT=iw, FMT="(/,T2,A,1X,ES11.4)") &
     816            0 :             "GAUXC| Molecular XC virial finite-difference dx", dx
     817              :          WRITE (UNIT=iw, FMT="(T2,A,3(1X,ES19.11))") &
     818            0 :             "GAUXC| Molecular XC virial FD 1/3 Trace", &
     819            0 :             analytic_trace, numerical_trace, diff_trace
     820              :       END IF
     821              : 
     822            0 :       DEALLOCATE (particle_set_minus, particle_set_plus)
     823              : 
     824              :    END SUBROUTINE debug_gauxc_molecular_virial
     825              : 
     826              : ! **************************************************************************************************
     827              : !> \brief prints a force-based molecular XC virial diagnostic from GauXC gradients
     828              : !> \param exc_grad ...
     829              : !> \param particle_set ...
     830              : !> \param para_env ...
     831              : ! **************************************************************************************************
     832            0 :    SUBROUTINE print_gauxc_molecular_virial(exc_grad, particle_set, para_env)
     833              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: exc_grad
     834              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     835              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     836              : 
     837              :       CHARACTER(len=1), DIMENSION(3), PARAMETER          :: label = ["x", "y", "z"]
     838              : 
     839              :       INTEGER                                            :: i, iatom, iw, j
     840              :       REAL(KIND=dp), DIMENSION(3)                        :: center, displacement, grad, grad_sum
     841              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: molecular_virial
     842              : 
     843            0 :       CPASSERT(ASSOCIATED(particle_set))
     844            0 :       CPASSERT(SIZE(exc_grad) == 3*SIZE(particle_set))
     845              : 
     846            0 :       IF (para_env%mepos /= 0) RETURN
     847              : 
     848            0 :       center = 0.0_dp
     849            0 :       DO iatom = 1, SIZE(particle_set)
     850            0 :          center = center + particle_set(iatom)%r
     851              :       END DO
     852            0 :       center = center/REAL(SIZE(particle_set), dp)
     853              : 
     854            0 :       grad_sum = 0.0_dp
     855            0 :       molecular_virial = 0.0_dp
     856            0 :       DO iatom = 1, SIZE(particle_set)
     857            0 :          grad = exc_grad(3*iatom - 2:3*iatom)
     858            0 :          displacement = particle_set(iatom)%r - center
     859            0 :          grad_sum = grad_sum + grad
     860            0 :          DO i = 1, 3
     861            0 :             DO j = 1, 3
     862            0 :                molecular_virial(i, j) = molecular_virial(i, j) + grad(i)*displacement(j)
     863              :             END DO
     864              :          END DO
     865              :       END DO
     866              : 
     867            0 :       iw = cp_logger_get_default_io_unit()
     868            0 :       IF (iw <= 0) RETURN
     869              : 
     870              :       WRITE (UNIT=iw, FMT="(/,T2,A)") &
     871            0 :          "GAUXC| Molecular XC gradient virial diagnostic [a.u.]"
     872            0 :       WRITE (UNIT=iw, FMT="(T2,A,T20,A,T40,A,T60,A)") "GAUXC|", "x", "y", "z"
     873            0 :       DO i = 1, 3
     874              :          WRITE (UNIT=iw, FMT="(T2,A,1X,A1,3(1X,ES19.11))") &
     875            0 :             "GAUXC|", label(i), molecular_virial(i, :)
     876              :       END DO
     877              :       WRITE (UNIT=iw, FMT="(T2,A,1X,ES19.11)") &
     878            0 :          "GAUXC| Molecular XC gradient virial 1/3 Trace", &
     879            0 :          (molecular_virial(1, 1) + molecular_virial(2, 2) + molecular_virial(3, 3))/3.0_dp
     880              :       WRITE (UNIT=iw, FMT="(T2,A,3(1X,ES19.11))") &
     881            0 :          "GAUXC| Molecular XC gradient sum", grad_sum
     882              :       WRITE (UNIT=iw, FMT="(T2,A)") &
     883            0 :          "GAUXC| Diagnostic only; this is not an analytical periodic stress tensor."
     884              : 
     885              :    END SUBROUTINE print_gauxc_molecular_virial
     886              : 
     887              : ! **************************************************************************************************
     888              : !> \brief Return information about the Skala functional
     889              : !> \param functional section containing the SKALA subsection
     890              : !> \param lsd if you are using lsd or lda
     891              : !> \param reference the reference to the article where the functional is explained
     892              : !> \param shortform the short definition of the functional
     893              : !> \param needs the flags corresponding to the inputs needed by this
     894              : !>        functional are set to true (the flags not needed aren't touched)
     895              : !> \param max_deriv the maximal derivative available
     896              : ! **************************************************************************************************
     897          957 :    SUBROUTINE skala_info(functional, lsd, reference, shortform, needs, max_deriv)
     898              :       TYPE(section_vals_type), POINTER                   :: functional
     899              :       LOGICAL, INTENT(in)                                :: lsd
     900              :       CHARACTER(LEN=*), INTENT(OUT), OPTIONAL            :: reference, shortform
     901              :       TYPE(xc_rho_cflags_type), INTENT(inout), OPTIONAL  :: needs
     902              :       INTEGER, INTENT(out), OPTIONAL                     :: max_deriv
     903              : 
     904              :       CHARACTER(len=default_path_length)                 :: model_key, model_name
     905              :       CHARACTER(len=default_string_length)               :: xc_fun_key, xc_fun_name
     906              :       INTEGER                                            :: gapw_representation
     907              :       LOGICAL                                            :: native_grid
     908              : 
     909          319 :       CALL section_vals_val_get(functional, "FUNCTIONAL", c_val=xc_fun_name)
     910          319 :       CALL section_vals_val_get(functional, "MODEL", c_val=model_name)
     911          319 :       CALL section_vals_val_get(functional, "NATIVE_GRID", l_val=native_grid)
     912              :       CALL section_vals_val_get(functional, "PSEUDOPOTENTIAL_GAPW_REPRESENTATION", &
     913          319 :                                 i_val=gapw_representation)
     914          319 :       native_grid = native_grid .OR. gapw_representation == skala_gapw_paw_one_center
     915          319 :       model_key = ADJUSTL(model_name)
     916          319 :       xc_fun_key = ADJUSTL(xc_fun_name)
     917          319 :       CALL uppercase(model_key)
     918          319 :       CALL uppercase(xc_fun_key)
     919              : 
     920          319 :       IF (PRESENT(reference)) THEN
     921            5 :          IF (TRIM(model_key) == "NONE" .OR. TRIM(model_key) == "" .OR. &
     922              :              TRIM(model_key) == TRIM(xc_fun_key)) THEN
     923            0 :             reference = "Functional computed by GauXC (underlying: "//TRIM(xc_fun_name)//")"
     924              :          ELSE
     925            5 :             reference = "Functional computed by GauXC Skala model "//TRIM(model_name)
     926              :          END IF
     927              :       END IF
     928          319 :       IF (PRESENT(shortform)) THEN
     929            5 :          IF (TRIM(model_key) == "NONE" .OR. TRIM(model_key) == "" .OR. &
     930              :              TRIM(model_key) == TRIM(xc_fun_key)) THEN
     931            0 :             shortform = "GAUXC ("//TRIM(xc_fun_name)//")"
     932              :          ELSE
     933            5 :             shortform = "GAUXC Skala"
     934              :          END IF
     935              :       END IF
     936          319 :       IF (PRESENT(needs)) THEN
     937          314 :          IF (native_grid .AND. TRIM(model_key) /= "NONE" .AND. TRIM(model_key) /= "" .AND. &
     938              :              TRIM(model_key) /= TRIM(xc_fun_key)) THEN
     939          276 :             IF (lsd) THEN
     940           16 :                needs%rho_spin = .TRUE.
     941           16 :                needs%drho_spin = .TRUE.
     942           16 :                needs%tau_spin = .TRUE.
     943              :             ELSE
     944          260 :                needs%rho = .TRUE.
     945          260 :                needs%drho = .TRUE.
     946          260 :                needs%tau = .TRUE.
     947              :             END IF
     948              :          ELSE
     949           38 :             needs%rho = .TRUE.
     950           38 :             IF (lsd) THEN
     951            8 :                needs%rho_spin = .TRUE.
     952              :             END IF
     953              :          END IF
     954              :       END IF
     955          319 :       IF (PRESENT(max_deriv)) max_deriv = 1
     956              : 
     957          319 :    END SUBROUTINE skala_info
     958              : 
     959              : ! GauXC uses replicated dense density and VXC matrices. The DBCSR density matrix
     960              : ! is distributed over MPI ranks, so apply_gauxc allreduces the dense copy before
     961              : ! passing it to GauXC.
     962              : 
     963              : ! **************************************************************************************************
     964              : !> \brief ...
     965              : !> \param qs_env ...
     966              : !> \param xc_section ...
     967              : !> \param calculate_forces ...
     968              : ! **************************************************************************************************
     969          478 :    SUBROUTINE apply_gauxc(qs_env, xc_section, calculate_forces)
     970              :       TYPE(qs_environment_type), INTENT(in), POINTER     :: qs_env
     971              :       TYPE(section_vals_type), INTENT(in), POINTER       :: xc_section
     972              :       LOGICAL, INTENT(IN)                                :: calculate_forces
     973              : 
     974              :       CHARACTER(len=*), PARAMETER :: nonlocal_vdw_abort_message = &
     975              :          "GauXC does not support non-local VDW_POTENTIAL corrections. "// &
     976              :          "Use an additive PAIR_POTENTIAL dispersion correction or disable GauXC."
     977              :       REAL(KIND=dp), PARAMETER :: gapw_fd_gradient_dx = 1.0E-4_dp
     978              : 
     979              :       CHARACTER(len=default_path_length)                 :: model_key, model_name, output_path
     980              :       CHARACTER(len=default_string_length) :: gradient_runtime, gradient_runtime_key, grid_key, &
     981              :          int_exec_space_key, lwd_kernel_key, pruning_key, skala_runtime, skala_runtime_key, &
     982              :          xc_fun_key
     983              :       INTEGER                                            :: atom_chunk_size, env_status, img, ispin, &
     984              :                                                             nimages
     985              :       LOGICAL :: atom_chunk_size_explicit, do_kpoints, gapw_method, gapw_paw_pseudopotentials, &
     986              :          gapw_pseudopotentials, grid_explicit, hdf5_output, is_periodic, molecular_virial, &
     987              :          molecular_virial_debug, need_xc_gradient, periodic_reference, pruning_explicit, &
     988              :          use_skala_model, write_hdf5_output
     989              :       REAL(KIND=dp)                                      :: molecular_virial_debug_dx
     990          478 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: density_scalar, density_zeta
     991          478 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     992              :       TYPE(cell_type), POINTER                           :: cell
     993              :       TYPE(cp_gauxc_cache_params)                        :: params
     994              :       TYPE(cp_gauxc_cache_type), POINTER                 :: cache
     995              :       TYPE(cp_gauxc_status_type)                         :: gauxc_status
     996          478 :       TYPE(cp_gauxc_xc_gradient_type)                    :: exc_grad
     997          478 :       TYPE(cp_gauxc_xc_type)                             :: gauxc_xc_result
     998              :       TYPE(dbcsr_p_type)                                 :: vxc_zeta_tmp
     999          478 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_vxc
    1000          478 :       TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER       :: rho_ao
    1001              :       TYPE(dft_control_type), POINTER                    :: dft_control
    1002              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1003          478 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
    1004              :       TYPE(qs_energy_type), POINTER                      :: energy
    1005          478 :       TYPE(qs_force_type), DIMENSION(:), POINTER         :: force
    1006          478 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
    1007              :       TYPE(qs_ks_env_type), POINTER                      :: ks_env
    1008              :       TYPE(qs_rho_type), POINTER                         :: rho, rho_use, rho_xc
    1009              :       TYPE(qs_scf_env_type), POINTER                     :: scf_env
    1010              :       TYPE(section_vals_type), POINTER                   :: gauxc_functional_section
    1011              : 
    1012              :       NULLIFY ( &
    1013              :          atomic_kind_set, &
    1014          478 :          cell, &
    1015          478 :          dft_control, &
    1016          478 :          energy, &
    1017          478 :          force, &
    1018          478 :          ks_env, &
    1019          478 :          matrix_vxc, &
    1020          478 :          para_env, &
    1021          478 :          particle_set, &
    1022          478 :          qs_kind_set, &
    1023          478 :          rho, &
    1024          478 :          rho_use, &
    1025          478 :          rho_xc, &
    1026          478 :          rho_ao, &
    1027          478 :          scf_env)
    1028              : 
    1029              :       CALL get_qs_env( &
    1030              :          qs_env, &
    1031              :          cell=cell, &
    1032              :          dft_control=dft_control, &
    1033              :          do_kpoints=do_kpoints, &
    1034              :          energy=energy, &
    1035              :          ks_env=ks_env, &
    1036              :          matrix_vxc=matrix_vxc, &
    1037              :          natom=params%natom, &
    1038              :          atomic_kind_set=atomic_kind_set, &
    1039              :          force=force, &
    1040              :          para_env=para_env, &
    1041              :          particle_set=particle_set, &
    1042              :          qs_kind_set=qs_kind_set, &
    1043              :          rho=rho, &
    1044              :          rho_xc=rho_xc, &
    1045          478 :          scf_env=scf_env)
    1046              : 
    1047          478 :       gapw_method = dft_control%qs_control%gapw .OR. dft_control%qs_control%gapw_xc
    1048              :       gapw_pseudopotentials = gapw_method .AND. &
    1049          110 :                               gauxc_gapw_has_pseudopotentials(qs_kind_set)
    1050              :       gapw_paw_pseudopotentials = gapw_method .AND. &
    1051          110 :                                   gauxc_gapw_has_paw_pseudopotentials(qs_kind_set)
    1052          478 :       IF (dft_control%qs_control%gapw_xc) THEN
    1053            0 :          CPASSERT(ASSOCIATED(rho_xc))
    1054            0 :          rho_use => rho_xc
    1055              :       ELSE
    1056          478 :          CPASSERT(ASSOCIATED(rho))
    1057          478 :          rho_use => rho
    1058              :       END IF
    1059              :       CALL qs_rho_get( &
    1060              :          rho_use, &
    1061          478 :          rho_ao_kp=rho_ao)
    1062              : 
    1063          478 :       nimages = dft_control%nimages
    1064          478 :       params%nspins = dft_control%nspins
    1065          478 :       is_periodic = .FALSE.
    1066          814 :       IF (ASSOCIATED(cell)) is_periodic = ANY(cell%perd /= 0)
    1067              : 
    1068          478 :       IF (ASSOCIATED(qs_env%dispersion_env)) THEN
    1069          478 :          IF (qs_env%dispersion_env%type == xc_vdw_fun_nonloc) THEN
    1070            0 :             CPABORT(nonlocal_vdw_abort_message)
    1071              :          END IF
    1072              :       END IF
    1073          478 :       NULLIFY (vxc_zeta_tmp%matrix)
    1074              : 
    1075          478 :       gauxc_functional_section => get_gauxc_functional(xc_section)
    1076              :       CALL section_vals_val_get( &
    1077              :          gauxc_functional_section, &
    1078              :          "FUNCTIONAL", &
    1079          478 :          c_val=params%xc_fun_name)
    1080              :       CALL section_vals_val_get( &
    1081              :          gauxc_functional_section, &
    1082              :          "MODEL", &
    1083          478 :          c_val=model_name)
    1084              :       CALL section_vals_val_get( &
    1085              :          gauxc_functional_section, &
    1086              :          "GRID", &
    1087              :          c_val=params%grid_type, &
    1088          478 :          explicit=grid_explicit)
    1089              :       CALL section_vals_val_get( &
    1090              :          gauxc_functional_section, &
    1091              :          "RADIAL_QUADRATURE", &
    1092          478 :          c_val=params%radial_quadrature)
    1093              :       CALL section_vals_val_get( &
    1094              :          gauxc_functional_section, &
    1095              :          "PRUNING_SCHEME", &
    1096              :          c_val=params%pruning_scheme, &
    1097          478 :          explicit=pruning_explicit)
    1098              :       CALL section_vals_val_get( &
    1099              :          gauxc_functional_section, &
    1100              :          "BATCH_SIZE", &
    1101          478 :          i_val=params%batch_size)
    1102              :       CALL section_vals_val_get( &
    1103              :          gauxc_functional_section, &
    1104              :          "DEVICE_RUNTIME_FILL_FRACTION", &
    1105          478 :          r_val=params%device_runtime_fill_fraction)
    1106              :       CALL section_vals_val_get( &
    1107              :          gauxc_functional_section, &
    1108              :          "MODEL_ATOM_CHUNK_SIZE", &
    1109              :          i_val=atom_chunk_size, &
    1110          478 :          explicit=atom_chunk_size_explicit)
    1111              :       CALL section_vals_val_get( &
    1112              :          gauxc_functional_section, &
    1113              :          "PERIODIC_REFERENCE", &
    1114          478 :          l_val=periodic_reference)
    1115              :       CALL section_vals_val_get( &
    1116              :          gauxc_functional_section, &
    1117              :          "MOLECULAR_VIRIAL", &
    1118          478 :          l_val=molecular_virial)
    1119              :       CALL section_vals_val_get( &
    1120              :          gauxc_functional_section, &
    1121              :          "MOLECULAR_VIRIAL_DEBUG", &
    1122          478 :          l_val=molecular_virial_debug)
    1123              :       CALL section_vals_val_get( &
    1124              :          gauxc_functional_section, &
    1125              :          "MOLECULAR_VIRIAL_DEBUG_DX", &
    1126          478 :          r_val=molecular_virial_debug_dx)
    1127              :       CALL section_vals_val_get( &
    1128              :          gauxc_functional_section, &
    1129              :          "LB_EXECUTION_SPACE", &
    1130          478 :          c_val=params%lb_exec_space)
    1131              :       CALL section_vals_val_get( &
    1132              :          gauxc_functional_section, &
    1133              :          "INT_EXECUTION_SPACE", &
    1134          478 :          c_val=params%int_exec_space)
    1135              :       CALL section_vals_val_get( &
    1136              :          gauxc_functional_section, &
    1137              :          "LWD_KERNEL", &
    1138          478 :          c_val=params%lwd_kernel)
    1139              :       CALL section_vals_val_get( &
    1140              :          gauxc_functional_section, &
    1141              :          "SKALA_RUNTIME", &
    1142          478 :          c_val=skala_runtime)
    1143              :       CALL section_vals_val_get( &
    1144              :          gauxc_functional_section, &
    1145              :          "MODEL_GRADIENT_RUNTIME", &
    1146          478 :          c_val=gradient_runtime)
    1147              :       CALL section_vals_val_get( &
    1148              :          gauxc_functional_section, &
    1149              :          "OUTPUT_PATH", &
    1150          478 :          c_val=output_path)
    1151              : 
    1152          478 :       model_key = ADJUSTL(model_name)
    1153          478 :       CALL uppercase(model_key)
    1154          478 :       xc_fun_key = ADJUSTL(params%xc_fun_name)
    1155          478 :       CALL uppercase(xc_fun_key)
    1156          478 :       skala_runtime_key = ADJUSTL(skala_runtime)
    1157          478 :       CALL uppercase(skala_runtime_key)
    1158          478 :       gradient_runtime_key = ADJUSTL(gradient_runtime)
    1159          478 :       CALL uppercase(gradient_runtime_key)
    1160          478 :       int_exec_space_key = ADJUSTL(params%int_exec_space)
    1161          478 :       CALL uppercase(int_exec_space_key)
    1162              :       params%use_gauxc_model = (TRIM(model_key) /= "" .AND. TRIM(model_key) /= "NONE" .AND. &
    1163          478 :                                 TRIM(model_key) /= TRIM(xc_fun_key))
    1164          478 :       use_skala_model = (INDEX(TRIM(model_key), "SKALA") > 0)
    1165          478 :       lwd_kernel_key = ADJUSTL(params%lwd_kernel)
    1166          478 :       CALL uppercase(lwd_kernel_key)
    1167          478 :       IF (TRIM(lwd_kernel_key) == "AUTO") THEN
    1168          478 :          params%lwd_kernel = "DEFAULT"
    1169              : #if defined(GAUXC_HAS_CUTLASS)
    1170              :          IF (use_skala_model .AND. TRIM(params%int_exec_space) == "DEVICE") THEN
    1171              :             params%lwd_kernel = "SCHEME1-CUTLASS"
    1172              :          END IF
    1173              : #endif
    1174              :       END IF
    1175          478 :       params%model_eval_name = model_name
    1176          478 :       IF (.NOT. params%use_gauxc_model) THEN
    1177              :          ! MODEL NONE and MODEL equal to FUNCTIONAL select conventional GauXC.
    1178          450 :          params%model_eval_name = "NONE"
    1179              :       END IF
    1180              :       IF (gapw_pseudopotentials .AND. params%use_gauxc_model .AND. .NOT. dft_control%qs_control%gapw_xc .AND. &
    1181          478 :           .NOT. gapw_paw_pseudopotentials .AND. para_env%mepos == 0 .AND. ASSOCIATED(scf_env)) THEN
    1182            2 :          IF (scf_env%iter_count == 1) THEN
    1183              :             CALL cp_warn( &
    1184              :                __LOCATION__, &
    1185              :                "GauXC Skala with METHOD GAPW and GPW_TYPE pseudopotentials evaluates "// &
    1186              :                "the XC term directly on the molecular AO/valence density; no GAPW one-center "// &
    1187            2 :                "XC correction is used for those regular-grid kinds.")
    1188              :          END IF
    1189              :       END IF
    1190          478 :       IF (params%device_runtime_fill_fraction <= 0.0_dp .OR. params%device_runtime_fill_fraction > 1.0_dp) THEN
    1191              :          CALL cp_abort(__LOCATION__, &
    1192            0 :                        "GAUXC%DEVICE_RUNTIME_FILL_FRACTION must be > 0 and <= 1.")
    1193              :       END IF
    1194          478 :       IF (atom_chunk_size < -1) THEN
    1195              :          CALL cp_abort(__LOCATION__, &
    1196            0 :                        "GAUXC%MODEL_ATOM_CHUNK_SIZE must be -1, zero, or positive.")
    1197              :       END IF
    1198          478 :       IF (molecular_virial_debug) THEN
    1199            0 :          IF (molecular_virial_debug_dx <= 0.0_dp) THEN
    1200              :             CALL cp_abort(__LOCATION__, &
    1201            0 :                           "GauXC MOLECULAR_VIRIAL_DEBUG_DX must be positive.")
    1202              :          END IF
    1203            0 :          molecular_virial = .TRUE.
    1204              :       END IF
    1205          478 :       need_xc_gradient = calculate_forces .OR. molecular_virial
    1206              :       CALL ensure_gauxc_periodic_reference_scope( &
    1207          478 :          dft_control, cell, qs_kind_set, do_kpoints, periodic_reference)
    1208          478 :       IF (is_periodic .AND. periodic_reference .AND. para_env%mepos == 0) THEN
    1209          219 :          IF (ASSOCIATED(scf_env)) THEN
    1210          219 :             IF (scf_env%iter_count == 1) THEN
    1211              :                CALL cp_warn( &
    1212              :                   __LOCATION__, &
    1213              :                   "GAUXC%PERIODIC_REFERENCE uses GauXC molecular quadrature for isolated validation "// &
    1214           30 :                   "cells. Compact periodic materials require a dedicated periodic GauXC interface.")
    1215              :             END IF
    1216              :          END IF
    1217              :       END IF
    1218          478 :       IF (params%use_gauxc_model) THEN
    1219           28 :          IF (has_nlcc(qs_kind_set)) THEN
    1220              :             CALL cp_abort(__LOCATION__, &
    1221              :                           "GauXC Skala with NLCC pseudopotentials is not implemented. "// &
    1222            0 :                           "The frozen core density would need a SKALA-consistent feature definition.")
    1223              :          END IF
    1224              :       END IF
    1225          478 :       IF (params%use_gauxc_model) THEN
    1226              :          CALL set_gauxc_model_atom_chunk_env( &
    1227           28 :             atom_chunk_size, atom_chunk_size_explicit)
    1228           28 :          IF (.NOT. grid_explicit) params%grid_type = "SUPERFINE"
    1229           28 :          IF (.NOT. pruning_explicit) params%pruning_scheme = "UNPRUNED"
    1230              : 
    1231           28 :          grid_key = ADJUSTL(params%grid_type)
    1232           28 :          pruning_key = ADJUSTL(params%pruning_scheme)
    1233           28 :          CALL uppercase(grid_key)
    1234           28 :          CALL uppercase(pruning_key)
    1235           28 :          IF (use_skala_model .AND. need_xc_gradient .AND. &
    1236              :              (TRIM(grid_key) /= "SUPERFINE" .OR. TRIM(pruning_key) /= "UNPRUNED")) THEN
    1237              :             CALL cp_warn( &
    1238              :                __LOCATION__, &
    1239              :                "GauXC Skala nuclear gradients are sensitive to the GauXC molecular grid. "// &
    1240            0 :                "Use GRID SUPERFINE and PRUNING_SCHEME UNPRUNED for quantitative force checks.")
    1241              :          END IF
    1242           28 :          IF (TRIM(model_key) == "SKALA") THEN
    1243           28 :             model_name = ""
    1244           28 :             env_status = 1
    1245           28 :             IF (TRIM(int_exec_space_key) == "DEVICE") THEN
    1246            0 :                CALL GET_ENVIRONMENT_VARIABLE("GAUXC_SKALA_CUDA_MODEL", model_name, STATUS=env_status)
    1247              :             END IF
    1248           28 :             IF (env_status /= 0 .OR. LEN_TRIM(model_name) == 0) THEN
    1249           28 :                CALL GET_ENVIRONMENT_VARIABLE("GAUXC_SKALA_MODEL", model_name, STATUS=env_status)
    1250              :             END IF
    1251           28 :             IF (env_status /= 0 .OR. LEN_TRIM(model_name) == 0) THEN
    1252            0 :                IF (TRIM(int_exec_space_key) == "DEVICE") THEN
    1253              :                   CALL cp_abort( &
    1254              :                      __LOCATION__, &
    1255              :                      "MODEL SKALA with DEVICE execution requires GAUXC_SKALA_CUDA_MODEL or "// &
    1256            0 :                      "GAUXC_SKALA_MODEL")
    1257              :                ELSE
    1258            0 :                   CPABORT("MODEL SKALA requires the GAUXC_SKALA_MODEL environment variable")
    1259              :                END IF
    1260              :             END IF
    1261           28 :             params%model_eval_name = model_name
    1262              :          END IF
    1263              :       END IF
    1264          956 :       SELECT CASE (TRIM(skala_runtime_key))
    1265              :       CASE ("AUTO")
    1266          498 :          params%use_self_runtime = use_skala_model .AND. para_env%num_pe > 1 .AND. params%nspins > 1
    1267              :       CASE ("MPI")
    1268            0 :          params%use_self_runtime = .FALSE.
    1269              :       CASE ("SELF")
    1270            0 :          params%use_self_runtime = use_skala_model .AND. para_env%num_pe > 1
    1271              :       CASE DEFAULT
    1272          478 :          CALL cp_abort(__LOCATION__, "Unknown GAUXC%SKALA_RUNTIME value.")
    1273              :       END SELECT
    1274          478 :       IF (.NOT. use_skala_model) params%use_self_runtime = .FALSE.
    1275          956 :       SELECT CASE (TRIM(gradient_runtime_key))
    1276              :       CASE ("AUTO", "SELF")
    1277          478 :          params%use_gradient_mpi_runtime = .FALSE.
    1278              :          params%use_gradient_self_runtime = need_xc_gradient .AND. params%use_gauxc_model .AND. &
    1279          956 :                                             para_env%num_pe > 1 .AND. .NOT. params%use_self_runtime
    1280              :       CASE ("MPI")
    1281            0 :          params%use_gradient_mpi_runtime = need_xc_gradient .AND. params%use_gauxc_model .AND. para_env%num_pe > 1
    1282            0 :          params%use_gradient_self_runtime = .FALSE.
    1283              :       CASE DEFAULT
    1284          478 :          CALL cp_abort(__LOCATION__, "Unknown GAUXC%MODEL_GRADIENT_RUNTIME value.")
    1285              :       END SELECT
    1286          478 :       IF (.NOT. params%use_gauxc_model) THEN
    1287          450 :          params%use_gradient_mpi_runtime = .FALSE.
    1288          450 :          params%use_gradient_self_runtime = .FALSE.
    1289              :       END IF
    1290              :       IF (use_skala_model .AND. para_env%num_pe > 1 .AND. .NOT. params%use_self_runtime .AND. &
    1291          478 :           para_env%mepos == 0 .AND. ASSOCIATED(scf_env)) THEN
    1292           10 :          IF (scf_env%iter_count == 1) THEN
    1293              :             CALL cp_warn( &
    1294              :                __LOCATION__, &
    1295              :                "GAUXC%SKALA_RUNTIME uses the MPI communicator for energy/VXC. "// &
    1296              :                "SKALA Torch atom chunks can be distributed across MPI ranks; "// &
    1297           10 :                "set GAUXC_ONEDFT_DISTRIBUTED_TORCH=0 to force rank-0 Torch inference.")
    1298              :          END IF
    1299              :       END IF
    1300              : 
    1301              :       ! After creating the basisset, we will have to check max_l>3 as a further condition
    1302          478 :       params%use_fd_gradient = gapw_method .AND. need_xc_gradient
    1303              : 
    1304          478 :       IF (.NOT. ASSOCIATED(qs_env%gauxc_cache)) ALLOCATE (qs_env%gauxc_cache)
    1305          478 :       cache => qs_env%gauxc_cache
    1306              :       CALL gauxc_cache_init( &
    1307              :          cache, &
    1308              :          params, &
    1309              :          para_env, &
    1310              :          particle_set, &
    1311              :          qs_kind_set, &
    1312          478 :          gauxc_status)
    1313              : 
    1314          478 :       hdf5_output = (TRIM(output_path) /= "")
    1315          478 :       write_hdf5_output = hdf5_output .AND. para_env%mepos == 0
    1316            0 :       IF (write_hdf5_output .AND. ASSOCIATED(scf_env)) THEN
    1317            0 :          write_hdf5_output = scf_env%iter_count == 1
    1318              :       END IF
    1319            0 :       IF (write_hdf5_output) THEN
    1320              :          CALL gauxc_write_molecule_hdf5( &
    1321              :             cache%molecule, &
    1322              :             output_path, &
    1323              :             "molecule.h5", &
    1324              :             "molecule", &
    1325            0 :             gauxc_status)
    1326            0 :          CALL gauxc_check_status(gauxc_status)
    1327              :          CALL gauxc_write_basisset_hdf5( &
    1328              :             cache%basisset, &
    1329              :             output_path, &
    1330              :             "basisset.h5", &
    1331              :             "basisset", &
    1332            0 :             gauxc_status)
    1333            0 :          CALL gauxc_check_status(gauxc_status)
    1334              :       END IF
    1335              : 
    1336          478 :       IF (qs_env%run_rtp) THEN
    1337            0 :          CPABORT("GAUXC XC energy currently does not support real-time propagation")
    1338              :       END IF
    1339              : 
    1340          478 :       energy%exc = 0
    1341              : 
    1342          478 :       IF (ASSOCIATED(matrix_vxc)) CALL dbcsr_deallocate_matrix_set(matrix_vxc)
    1343          478 :       CALL dbcsr_allocate_matrix_set(matrix_vxc, params%nspins)
    1344              : 
    1345          956 :       DO img = 1, nimages
    1346          478 :          IF (img > 1) THEN
    1347            0 :             CPABORT("UNIMPLEMENTED: Handling nimg>1 in k-point integration")
    1348              :          END IF
    1349          478 :          CALL dbcsr_to_dense(rho_ao(1, img), density_scalar, para_env)
    1350          478 :          CALL para_env%sum(density_scalar)
    1351          478 :          IF (params%nspins == 1) THEN
    1352              :             gauxc_xc_result = gauxc_compute_xc( &
    1353              :                               cache%integrator, &
    1354              :                               density_scalar, &
    1355              :                               nspins=params%nspins, &
    1356              :                               status=gauxc_status, &
    1357          456 :                               model=TRIM(params%model_eval_name))
    1358          456 :             CALL gauxc_check_status(gauxc_status)
    1359          456 :             IF (need_xc_gradient) THEN
    1360            6 :                IF (params%use_fd_gradient) THEN
    1361              :                   CALL gauxc_xc_gradient_fd( &
    1362              :                      particle_set, qs_kind_set, density_scalar, params%nspins, params%model_eval_name, &
    1363              :                      params%xc_fun_name, params%grid_type, params%radial_quadrature, params%pruning_scheme, &
    1364              :                      params%lb_exec_space, params%int_exec_space, params%lwd_kernel, params%batch_size, &
    1365              :                      params%device_runtime_fill_fraction, gapw_fd_gradient_dx, para_env, &
    1366            2 :                      exc_grad%exc_grad)
    1367            4 :                ELSE IF (params%use_gradient_self_runtime) THEN
    1368              :                   exc_grad = gauxc_compute_xc_gradient( &
    1369              :                              cache%gradient_integrator, &
    1370              :                              density_scalar, &
    1371              :                              nspins=params%nspins, &
    1372              :                              natom=params%natom, &
    1373              :                              status=gauxc_status, &
    1374            0 :                              model=TRIM(params%model_eval_name))
    1375              :                ELSE
    1376              :                   exc_grad = gauxc_compute_xc_gradient( &
    1377              :                              cache%integrator, &
    1378              :                              density_scalar, &
    1379              :                              nspins=params%nspins, &
    1380              :                              natom=params%natom, &
    1381              :                              status=gauxc_status, &
    1382            4 :                              model=TRIM(params%model_eval_name))
    1383              :                END IF
    1384            6 :                CALL gauxc_check_status(gauxc_status)
    1385            6 :                IF (calculate_forces) THEN
    1386              :                   CALL add_gauxc_gradient_to_force( &
    1387              :                      exc_grad%exc_grad, &
    1388              :                      force, &
    1389              :                      atomic_kind_set, &
    1390            6 :                      para_env)
    1391              :                END IF
    1392            6 :                IF (molecular_virial) THEN
    1393            0 :                   CALL print_gauxc_molecular_virial(exc_grad%exc_grad, particle_set, para_env)
    1394              :                END IF
    1395            6 :                IF (molecular_virial_debug) THEN
    1396              :                   CALL debug_gauxc_molecular_virial( &
    1397              :                      exc_grad%exc_grad, particle_set, qs_kind_set, density_scalar, params%nspins, &
    1398              :                     params%model_eval_name, params%xc_fun_name, params%grid_type, params%radial_quadrature, params%pruning_scheme, &
    1399              :                      params%lb_exec_space, params%int_exec_space, params%lwd_kernel, params%batch_size, &
    1400            0 :                      params%device_runtime_fill_fraction, molecular_virial_debug_dx, para_env)
    1401              :                END IF
    1402            6 :                DEALLOCATE (exc_grad%exc_grad)
    1403              :             END IF
    1404              :          ELSE
    1405           22 :             CPASSERT(params%nspins == 2)
    1406              :             ! In here:
    1407              :             ! scalar <- rho_ao(1, :) + rho_ao(2, :)
    1408              :             ! zeta   <- rho_ao(1, :) - rho_ao(2, :)
    1409           22 :             CALL dbcsr_to_dense(rho_ao(2, img), density_zeta, para_env)
    1410           22 :             CALL para_env%sum(density_zeta)
    1411              :             ! Do NOT reorder the following lines!
    1412         5330 :             density_scalar(:, :) = density_scalar(:, :) + density_zeta(:, :)
    1413              :             ! Factor two because the next line is evaluated after the above line.
    1414              :             ! We need to subtract density_zeta once to undo the above line and
    1415              :             ! a second time because that is what UKS requires.
    1416              :             ! This style lowers memory footprint.
    1417         5330 :             density_zeta(:, :) = density_scalar(:, :) - 2.0_dp*density_zeta(:, :)
    1418              :             gauxc_xc_result = gauxc_compute_xc( &
    1419              :                               cache%integrator, &
    1420              :                               density_scalar, &
    1421              :                               density_zeta, &
    1422              :                               params%nspins, &
    1423              :                               gauxc_status, &
    1424           22 :                               model=TRIM(params%model_eval_name))
    1425           22 :             CALL gauxc_check_status(gauxc_status)
    1426           22 :             IF (need_xc_gradient) THEN
    1427            0 :                IF (params%use_fd_gradient) THEN
    1428              :                   CALL gauxc_xc_gradient_fd( &
    1429              :                      particle_set, qs_kind_set, density_scalar, params%nspins, params%model_eval_name, &
    1430              :                      params%xc_fun_name, params%grid_type, params%radial_quadrature, params%pruning_scheme, &
    1431              :                      params%lb_exec_space, params%int_exec_space, params%lwd_kernel, params%batch_size, &
    1432              :                      params%device_runtime_fill_fraction, gapw_fd_gradient_dx, para_env, &
    1433            0 :                      exc_grad%exc_grad, density_zeta=density_zeta)
    1434            0 :                ELSE IF (params%use_gradient_self_runtime) THEN
    1435              :                   exc_grad = gauxc_compute_xc_gradient( &
    1436              :                              cache%gradient_integrator, &
    1437              :                              density_scalar, &
    1438              :                              density_zeta, &
    1439              :                              params%nspins, &
    1440              :                              params%natom, &
    1441              :                              gauxc_status, &
    1442            0 :                              model=TRIM(params%model_eval_name))
    1443              :                ELSE
    1444              :                   exc_grad = gauxc_compute_xc_gradient( &
    1445              :                              cache%integrator, &
    1446              :                              density_scalar, &
    1447              :                              density_zeta, &
    1448              :                              params%nspins, &
    1449              :                              params%natom, &
    1450              :                              gauxc_status, &
    1451            0 :                              model=TRIM(params%model_eval_name))
    1452              :                END IF
    1453            0 :                CALL gauxc_check_status(gauxc_status)
    1454            0 :                IF (calculate_forces) THEN
    1455              :                   CALL add_gauxc_gradient_to_force( &
    1456              :                      exc_grad%exc_grad, &
    1457              :                      force, &
    1458              :                      atomic_kind_set, &
    1459            0 :                      para_env)
    1460              :                END IF
    1461            0 :                IF (molecular_virial) THEN
    1462            0 :                   CALL print_gauxc_molecular_virial(exc_grad%exc_grad, particle_set, para_env)
    1463              :                END IF
    1464            0 :                IF (molecular_virial_debug) THEN
    1465              :                   CALL debug_gauxc_molecular_virial( &
    1466              :                      exc_grad%exc_grad, particle_set, qs_kind_set, density_scalar, params%nspins, &
    1467              :                     params%model_eval_name, params%xc_fun_name, params%grid_type, params%radial_quadrature, params%pruning_scheme, &
    1468              :                      params%lb_exec_space, params%int_exec_space, params%lwd_kernel, params%batch_size, &
    1469              :                      params%device_runtime_fill_fraction, molecular_virial_debug_dx, para_env, &
    1470            0 :                      density_zeta=density_zeta)
    1471              :                END IF
    1472            0 :                DEALLOCATE (exc_grad%exc_grad)
    1473              :             END IF
    1474              :          END IF
    1475              : 
    1476          478 :          energy%exc = energy%exc + gauxc_xc_result%exc
    1477              : 
    1478          956 :          IF (params%nspins == 1) THEN
    1479          456 :             IF (img == 1) THEN
    1480          456 :                matrix_vxc(1) = dense_to_dbcsr(gauxc_xc_result%vxc_scalar, rho_ao(1, img))
    1481              :             ELSE
    1482            0 :                CPABORT("UNIMPLEMENTED: Handling multiple result matrices in k-point integration")
    1483              :             END IF
    1484              :          ELSE
    1485           22 :             CPASSERT(params%nspins == 2)
    1486              :             ! Transform derivatives from total/spin density back to alpha/beta channels.
    1487           22 :             vxc_zeta_tmp = dense_to_dbcsr(gauxc_xc_result%vxc_zeta, rho_ao(1, img))
    1488           22 :             IF (img == 1) THEN
    1489           66 :                DO ispin = 1, 2
    1490           44 :                   matrix_vxc(ispin) = dense_to_dbcsr(gauxc_xc_result%vxc_scalar, rho_ao(ispin, 1))
    1491              :                   CALL dbcsr_add( &
    1492              :                      matrix_vxc(ispin)%matrix, &
    1493              :                      vxc_zeta_tmp%matrix, &
    1494              :                      1.0_dp, &
    1495              :                      ! 1.0 for ispin==1, -1.0 for ispin==2
    1496           66 :                      1.0_dp - REAL(ispin - 1, dp)*2.0_dp)
    1497              :                END DO
    1498              :             ELSE
    1499            0 :                CPABORT("UNIMPLEMENTED: Handling multiple result matrices in k-point integration")
    1500              :             END IF
    1501           22 :             CALL dbcsr_release(vxc_zeta_tmp%matrix)
    1502           22 :             DEALLOCATE (vxc_zeta_tmp%matrix)
    1503              :          END IF
    1504              :       END DO
    1505              : 
    1506          478 :       DEALLOCATE (density_scalar)
    1507          478 :       IF (ALLOCATED(density_zeta)) DEALLOCATE (density_zeta)
    1508          478 :       DEALLOCATE (gauxc_xc_result%vxc_scalar)
    1509          478 :       IF (ALLOCATED(gauxc_xc_result%vxc_zeta)) DEALLOCATE (gauxc_xc_result%vxc_zeta)
    1510              : 
    1511          478 :       CALL set_ks_env(ks_env, matrix_vxc=matrix_vxc)
    1512          978 :       DO ispin = 1, params%nspins
    1513          978 :          CALL dbcsr_finalize(matrix_vxc(ispin)%matrix)
    1514              :       END DO
    1515              : 
    1516          956 :    END SUBROUTINE apply_gauxc
    1517              : 
    1518              : END MODULE xc_gauxc_functional
        

Generated by: LCOV version 2.0-1