LCOV - code coverage report
Current view: top level - src - qs_apt_fdiff_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:936074a) Lines: 98.9 % 91 90
Test Date: 2025-12-04 06:27:48 Functions: 100.0 % 3 3

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2025 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief Atomic Polarization Tensor calculation by dF/d(E-field) finite differences
      10              : !> \author Leo Decking, Hossam Elgabarty
      11              : ! **************************************************************************************************
      12              : 
      13              : MODULE qs_apt_fdiff_methods
      14              :    USE cp_control_types,                ONLY: dft_control_type
      15              :    USE cp_log_handling,                 ONLY: cp_add_default_logger,&
      16              :                                               cp_get_default_logger,&
      17              :                                               cp_logger_create,&
      18              :                                               cp_logger_get_default_io_unit,&
      19              :                                               cp_logger_release,&
      20              :                                               cp_logger_set,&
      21              :                                               cp_logger_type,&
      22              :                                               cp_rm_default_logger
      23              :    USE cp_output_handling,              ONLY: cp_print_key_finished_output,&
      24              :                                               cp_print_key_unit_nr
      25              :    USE cp_subsys_types,                 ONLY: cp_subsys_get,&
      26              :                                               cp_subsys_type
      27              :    USE force_env_types,                 ONLY: force_env_get,&
      28              :                                               force_env_type
      29              :    USE input_section_types,             ONLY: section_vals_get_subs_vals,&
      30              :                                               section_vals_type,&
      31              :                                               section_vals_val_get
      32              :    USE kinds,                           ONLY: dp
      33              :    USE message_passing,                 ONLY: mp_para_env_type
      34              :    USE particle_list_types,             ONLY: particle_list_type
      35              :    USE qs_apt_fdiff_types,              ONLY: apt_fdiff_point_type,&
      36              :                                               apt_fdiff_points_type
      37              :    USE qs_environment_types,            ONLY: get_qs_env,&
      38              :                                               qs_environment_type
      39              :    USE qs_force,                        ONLY: qs_calc_energy_force
      40              :    USE qs_subsys_types,                 ONLY: qs_subsys_type
      41              : #include "./base/base_uses.f90"
      42              : 
      43              :    IMPLICIT NONE
      44              :    PRIVATE
      45              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_apt_fdiff_methods'
      46              :    LOGICAL, PARAMETER, PRIVATE :: debug_this_module = .FALSE.
      47              :    PUBLIC :: apt_fdiff
      48              : 
      49              : CONTAINS
      50              : 
      51              : ! **************************************************************************************************
      52              : !> \brief Perform the 2PNT symmetric difference
      53              : !> \param apt_fdiff_points ...
      54              : !> \param ap_tensor ...
      55              : !> \param natoms ...
      56              : !> \author Leo Decking
      57              : ! **************************************************************************************************
      58            2 :    SUBROUTINE fdiff_2pnt(apt_fdiff_points, ap_tensor, natoms)
      59              :       TYPE(apt_fdiff_points_type)                        :: apt_fdiff_points
      60              :       REAL(kind=dp), DIMENSION(:, :, :)                  :: ap_tensor
      61              :       INTEGER, INTENT(IN)                                :: natoms
      62              : 
      63              :       INTEGER                                            :: i, j, n
      64              : 
      65            8 :       DO j = 1, 3 ! axis force
      66           26 :          DO i = 1, 3 ! axis field
      67          132 :             DO n = 1, natoms
      68              :                ap_tensor(n, i, j) = (apt_fdiff_points%point_field(i, 1)%forces(n, j) - &
      69              :                                      apt_fdiff_points%point_field(i, 2)%forces(n, j)) &
      70          126 :                                     /(2*apt_fdiff_points%field_strength)
      71              :             END DO
      72              :          END DO
      73              :       END DO
      74              : 
      75            2 :    END SUBROUTINE fdiff_2pnt
      76              : 
      77              : ! **************************************************************************************************
      78              : !> \brief ...
      79              : !> \param apt_fdiff_point ...
      80              : !> \param particles ...
      81              : !> \author Leo Decking
      82              : ! **************************************************************************************************
      83           12 :    SUBROUTINE get_forces(apt_fdiff_point, particles)
      84              :       TYPE(apt_fdiff_point_type)                         :: apt_fdiff_point
      85              :       TYPE(particle_list_type), POINTER                  :: particles
      86              : 
      87              :       INTEGER                                            :: i
      88              : 
      89           12 :       CPASSERT(ASSOCIATED(particles))
      90              : 
      91           84 :       DO i = 1, particles%n_els
      92          300 :          apt_fdiff_point%forces(i, 1:3) = particles%els(i)%f(1:3)
      93              :       END DO
      94              : 
      95           12 :    END SUBROUTINE get_forces
      96              : 
      97              : ! **************************************************************************************************
      98              : !> \brief Calculate Atomic Polarization Tensors by dF/d(E-field) finite differences
      99              : !> \param force_env ...
     100              : !> \author Leo Decking, Hossam Elgabarty
     101              : ! **************************************************************************************************
     102            2 :    SUBROUTINE apt_fdiff(force_env)
     103              : 
     104              :       TYPE(force_env_type), POINTER                      :: force_env
     105              : 
     106              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'apt_fdiff'
     107              : 
     108              :       INTEGER                                            :: apt_log, fd_method, handle, i, j, &
     109              :                                                             log_unit, n, natoms, output_fdiff_scf
     110              :       REAL(kind=dp)                                      :: born_sum
     111            2 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :)     :: ap_tensor
     112           18 :       TYPE(apt_fdiff_points_type)                        :: apt_fdiff_points
     113              :       TYPE(cp_logger_type), POINTER                      :: logger, logger_apt
     114              :       TYPE(cp_subsys_type), POINTER                      :: cp_subsys
     115              :       TYPE(dft_control_type), POINTER                    :: dft_control
     116              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     117              :       TYPE(particle_list_type), POINTER                  :: particles
     118              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     119              :       TYPE(qs_subsys_type), POINTER                      :: subsys
     120              :       TYPE(section_vals_type), POINTER                   :: dcdr_section
     121              : 
     122            2 :       CALL timeset(routineN, handle)
     123              : 
     124            2 :       NULLIFY (qs_env, logger, dcdr_section, logger_apt, para_env, dft_control, subsys)
     125            2 :       NULLIFY (particles)
     126              : 
     127            2 :       CPASSERT(ASSOCIATED(force_env))
     128              : 
     129            2 :       CALL force_env_get(force_env, subsys=cp_subsys, qs_env=qs_env, para_env=para_env)
     130              : 
     131            2 :       CALL cp_subsys_get(cp_subsys, particles=particles)
     132            2 :       CPASSERT(ASSOCIATED(particles))
     133            2 :       CALL get_qs_env(qs_env=qs_env, dft_control=dft_control)!, para_env=para_env)
     134              : 
     135            2 :       natoms = particles%n_els
     136            2 :       IF (dft_control%apply_period_efield .OR. dft_control%apply_efield) THEN
     137            0 :          CPABORT("APT calculation not available in the presence of an external electric field")
     138              :       END IF
     139              : 
     140            2 :       logger => cp_get_default_logger()
     141              : 
     142            2 :       log_unit = cp_logger_get_default_io_unit(logger)
     143              : 
     144            2 :       dcdr_section => section_vals_get_subs_vals(qs_env%input, "PROPERTIES%LINRES%DCDR")
     145              : 
     146              :       apt_log = cp_print_key_unit_nr(logger, dcdr_section, "PRINT%APT", &
     147              :                                      extension=".data", middle_name="apt", log_filename=.FALSE., &
     148            2 :                                      file_position="APPEND", file_status="UNKNOWN")
     149              : 
     150              :       output_fdiff_scf = cp_print_key_unit_nr(logger, dcdr_section, "PRINT%APT", &
     151              :                                               extension=".scfLog", middle_name="apt", log_filename=.FALSE., &
     152            2 :                                               file_position="APPEND", file_status="UNKNOWN")
     153              : 
     154              :       CALL cp_logger_create(logger_apt, para_env=para_env, print_level=0, &
     155              :                             default_global_unit_nr=output_fdiff_scf, &
     156            2 :                             close_global_unit_on_dealloc=.TRUE.)
     157              : 
     158            2 :       CALL cp_logger_set(logger_apt, global_filename="APT_localLog")
     159              : 
     160            2 :       CALL cp_add_default_logger(logger_apt)
     161              : 
     162            2 :       IF (output_fdiff_scf > 0) THEN
     163              :          WRITE (output_fdiff_scf, '(T2,A)') &
     164            1 :             '!----------------------------------------------------------------------------!'
     165            1 :          WRITE (output_fdiff_scf, '(/,T2,A)') "SCF log for finite difference steps"
     166              :          WRITE (output_fdiff_scf, '(/,T2,A)') &
     167            1 :             '!----------------------------------------------------------------------------!'
     168              :       END IF
     169              : 
     170            2 :       IF (log_unit > 0) THEN
     171              :          WRITE (log_unit, '(/,T2,A)') &
     172            1 :             '!----------------------------------------------------------------------------!'
     173            1 :          WRITE (log_unit, '(/,T10,A)') "Computing Atomic polarization tensors using finite differences"
     174            1 :          WRITE (log_unit, '(T2,A)') "  "
     175              :       END IF
     176              : 
     177            2 :       CALL section_vals_val_get(dcdr_section, "APT_FD_DE", r_val=apt_fdiff_points%field_strength)
     178            2 :       CALL section_vals_val_get(dcdr_section, "APT_FD_METHOD", i_val=fd_method)
     179              : 
     180            2 :       dft_control%apply_period_efield = .TRUE.
     181           18 :       ALLOCATE (dft_control%period_efield)
     182            2 :       dft_control%period_efield%displacement_field = .FALSE.
     183              : 
     184            8 :       DO i = 1, 3
     185           24 :          dft_control%period_efield%polarisation(1:3) = [0.0_dp, 0.0_dp, 0.0_dp]
     186            6 :          dft_control%period_efield%polarisation(i) = 1.0_dp
     187              : 
     188            6 :          IF (log_unit > 0) THEN
     189            3 :             WRITE (log_unit, '(T2,A)') "  "
     190            3 :             WRITE (log_unit, "(T2,A)") "Computing forces under efield in direction +/-"//ACHAR(i + 119)
     191              :          END IF
     192              : 
     193           20 :          DO j = 1, 2 ! 1 -> positive, 2 -> negative
     194           12 :             IF (j == 1) THEN
     195            6 :                dft_control%period_efield%strength = apt_fdiff_points%field_strength
     196              :             ELSE
     197            6 :                dft_control%period_efield%strength = -1.0*apt_fdiff_points%field_strength
     198              :             END IF
     199              : 
     200           12 :             CALL qs_calc_energy_force(qs_env, calc_force=.TRUE., consistent_energies=.TRUE., linres=.FALSE.)
     201              : 
     202           36 :             ALLOCATE (apt_fdiff_points%point_field(i, j)%forces(natoms, 1:3))
     203           18 :             CALL get_forces(apt_fdiff_points%point_field(i, j), particles)
     204              : 
     205              :          END DO
     206              :       END DO
     207              : 
     208            2 :       IF (output_fdiff_scf > 0) THEN
     209              :          WRITE (output_fdiff_scf, '(/,T2,A)') &
     210            1 :             '!----------------------------------------------------------------------------!'
     211            1 :          WRITE (output_fdiff_scf, '(/,T2,A)') "Finite differences done!"
     212              :          WRITE (output_fdiff_scf, '(/,T2,A)') &
     213            1 :             '!----------------------------------------------------------------------------!'
     214              :       END IF
     215              : 
     216            2 :       CALL cp_print_key_finished_output(output_fdiff_scf, logger_apt, dcdr_section, "PRINT%APT")
     217            2 :       CALL cp_logger_release(logger_apt)
     218            2 :       CALL cp_rm_default_logger()
     219              : 
     220            8 :       ALLOCATE (ap_tensor(natoms, 3, 3))
     221            2 :       CALL fdiff_2pnt(apt_fdiff_points, ap_tensor, natoms)
     222              : 
     223              :       !Print
     224            2 :       born_sum = 0.0_dp
     225            2 :       IF (apt_log > 0) THEN
     226            7 :          DO n = 1, natoms
     227            6 :             born_sum = born_sum + (ap_tensor(n, 1, 1) + ap_tensor(n, 1, 1) + ap_tensor(n, 1, 1))/3.0
     228            6 :             WRITE (apt_log, "(I6, A6, F20.10)") n, particles%els(n)%atomic_kind%element_symbol, &
     229           12 :                (ap_tensor(n, 1, 1) + ap_tensor(n, 2, 2) + ap_tensor(n, 3, 3))/3.0
     230              :             WRITE (apt_log, '(F20.10,F20.10,F20.10)') &
     231            6 :                ap_tensor(n, 1, 1), ap_tensor(n, 1, 2), ap_tensor(n, 1, 3)
     232              :             WRITE (apt_log, '(F20.10,F20.10,F20.10)') &
     233            6 :                ap_tensor(n, 2, 1), ap_tensor(n, 2, 2), ap_tensor(n, 2, 3)
     234              :             WRITE (apt_log, '(F20.10,F20.10,F20.10)') &
     235            7 :                ap_tensor(n, 3, 1), ap_tensor(n, 3, 2), ap_tensor(n, 3, 3)
     236              :          END DO
     237            1 :          WRITE (apt_log, '(/,A20, F20.10)') "Sum of Born charges:", born_sum
     238            1 :          WRITE (log_unit, '(/,A30, F20.10)') "Checksum (Acoustic Sum Rule):", born_sum
     239              :       END IF
     240            2 :       DEALLOCATE (ap_tensor)
     241            2 :       DEALLOCATE (dft_control%period_efield)
     242              : 
     243            2 :       CALL cp_print_key_finished_output(apt_log, logger, dcdr_section, "PRINT%APT")
     244              : 
     245            2 :       dft_control%apply_period_efield = .FALSE.
     246            2 :       qs_env%linres_run = .FALSE.
     247              : 
     248            2 :       IF (log_unit > 0) THEN
     249            1 :          WRITE (log_unit, '(T2,A)') "  "
     250            1 :          WRITE (log_unit, '(T2,A)') "  "
     251            1 :          WRITE (log_unit, '(T22,A)') "APT calculation Done!"
     252              :          WRITE (log_unit, '(/,T2,A)') &
     253            1 :             '!----------------------------------------------------------------------------!'
     254              :       END IF
     255              : 
     256            2 :       CALL timestop(handle)
     257              : 
     258           18 :    END SUBROUTINE apt_fdiff
     259              : 
     260              : END MODULE qs_apt_fdiff_methods
        

Generated by: LCOV version 2.0-1