LCOV - code coverage report
Current view: top level - src - fist_efield_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:936074a) Lines: 91.2 % 136 124
Test Date: 2025-12-04 06:27:48 Functions: 100.0 % 2 2

            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              : !> \par History
      10              : !> \author JGH
      11              : ! **************************************************************************************************
      12              : MODULE fist_efield_methods
      13              :    USE atomic_kind_types,               ONLY: atomic_kind_type,&
      14              :                                               get_atomic_kind
      15              :    USE cell_types,                      ONLY: cell_type,&
      16              :                                               pbc
      17              :    USE cp_result_methods,               ONLY: cp_results_erase,&
      18              :                                               put_results
      19              :    USE cp_result_types,                 ONLY: cp_result_type
      20              :    USE fist_efield_types,               ONLY: fist_efield_type
      21              :    USE fist_environment_types,          ONLY: fist_env_get,&
      22              :                                               fist_environment_type
      23              :    USE input_section_types,             ONLY: section_get_ival,&
      24              :                                               section_vals_type,&
      25              :                                               section_vals_val_get
      26              :    USE kinds,                           ONLY: default_string_length,&
      27              :                                               dp
      28              :    USE mathconstants,                   ONLY: twopi,&
      29              :                                               z_one,&
      30              :                                               z_zero
      31              :    USE moments_utils,                   ONLY: get_reference_point
      32              :    USE particle_types,                  ONLY: particle_type
      33              :    USE physcon,                         ONLY: debye
      34              : #include "./base/base_uses.f90"
      35              : 
      36              :    IMPLICIT NONE
      37              : 
      38              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'fist_efield_methods'
      39              : 
      40              :    PRIVATE
      41              : 
      42              :    PUBLIC :: fist_dipole, fist_efield_energy_force
      43              : 
      44              : ! **************************************************************************************************
      45              : 
      46              : CONTAINS
      47              : 
      48              : ! **************************************************************************************************
      49              : !> \brief ...
      50              : !> \param qenergy ...
      51              : !> \param qforce ...
      52              : !> \param qpv ...
      53              : !> \param atomic_kind_set ...
      54              : !> \param particle_set ...
      55              : !> \param cell ...
      56              : !> \param efield ...
      57              : !> \param use_virial ...
      58              : !> \param iunit ...
      59              : !> \param charges ...
      60              : ! **************************************************************************************************
      61          116 :    SUBROUTINE fist_efield_energy_force(qenergy, qforce, qpv, atomic_kind_set, particle_set, cell, &
      62              :                                        efield, use_virial, iunit, charges)
      63              :       REAL(KIND=dp), INTENT(OUT)                         :: qenergy
      64              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: qforce
      65              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(OUT)        :: qpv
      66              :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
      67              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
      68              :       TYPE(cell_type), POINTER                           :: cell
      69              :       TYPE(fist_efield_type), POINTER                    :: efield
      70              :       LOGICAL, INTENT(IN), OPTIONAL                      :: use_virial
      71              :       INTEGER, INTENT(IN), OPTIONAL                      :: iunit
      72              :       REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER     :: charges
      73              : 
      74              :       COMPLEX(KIND=dp)                                   :: zeta
      75              :       COMPLEX(KIND=dp), DIMENSION(3)                     :: ggamma
      76              :       INTEGER                                            :: i, ii, iparticle_kind, iw, j
      77          116 :       INTEGER, DIMENSION(:), POINTER                     :: atom_list
      78              :       LOGICAL                                            :: use_charges, virial
      79              :       REAL(KIND=dp)                                      :: q, theta
      80              :       REAL(KIND=dp), DIMENSION(3)                        :: ci, dfilter, di, dipole, fieldpol, fq, &
      81              :                                                             gvec, ria, tmp
      82              :       TYPE(atomic_kind_type), POINTER                    :: atomic_kind
      83              : 
      84          116 :       qenergy = 0.0_dp
      85         1508 :       qforce = 0.0_dp
      86          116 :       qpv = 0.0_dp
      87              : 
      88          116 :       use_charges = .FALSE.
      89          116 :       IF (PRESENT(charges)) THEN
      90          116 :          IF (ASSOCIATED(charges)) use_charges = .TRUE.
      91              :       END IF
      92              : 
      93              :       IF (PRESENT(iunit)) THEN
      94          116 :          iw = iunit
      95              :       ELSE
      96          116 :          iw = -1
      97              :       END IF
      98              : 
      99          116 :       IF (PRESENT(use_virial)) THEN
     100          116 :          virial = use_virial
     101              :       ELSE
     102              :          virial = .FALSE.
     103              :       END IF
     104              : 
     105          464 :       fieldpol = efield%polarisation
     106          812 :       fieldpol = fieldpol/SQRT(DOT_PRODUCT(fieldpol, fieldpol))
     107          464 :       fieldpol = -fieldpol*efield%strength
     108              : 
     109          464 :       dfilter = efield%dfilter
     110              : 
     111          116 :       dipole = 0.0_dp
     112          464 :       ggamma = z_one
     113          348 :       DO iparticle_kind = 1, SIZE(atomic_kind_set)
     114          232 :          atomic_kind => atomic_kind_set(iparticle_kind)
     115          232 :          CALL get_atomic_kind(atomic_kind=atomic_kind, qeff=q, atom_list=atom_list)
     116              :          ! TODO parallelization over atoms (local_particles)
     117          696 :          DO i = 1, SIZE(atom_list)
     118          348 :             ii = atom_list(i)
     119         1392 :             ria = particle_set(ii)%r(:)
     120         1392 :             ria = pbc(ria, cell)
     121          348 :             IF (use_charges) q = charges(ii)
     122         1392 :             DO j = 1, 3
     123         4176 :                gvec = twopi*cell%h_inv(j, :)
     124         4176 :                theta = SUM(ria(:)*gvec(:))
     125         1044 :                zeta = CMPLX(COS(theta), SIN(theta), KIND=dp)**(-q)
     126         1392 :                ggamma(j) = ggamma(j)*zeta
     127              :             END DO
     128         1624 :             qforce(1:3, ii) = q
     129              :          END DO
     130              :       END DO
     131              : 
     132          464 :       IF (ALL(REAL(ggamma, KIND=dp) /= 0.0_dp)) THEN
     133          464 :          tmp = AIMAG(ggamma)/REAL(ggamma, KIND=dp)
     134          464 :          ci = ATAN(tmp)
     135         1856 :          dipole = MATMUL(cell%hmat, ci)/twopi
     136              :       END IF
     137              : 
     138          116 :       IF (efield%displacement) THEN
     139              :          ! E = (omega/8Pi)(D - 4Pi*P)^2
     140          152 :          di = dipole/cell%deth
     141          152 :          DO i = 1, 3
     142          114 :             theta = fieldpol(i) - 2._dp*twopi*di(i)
     143          114 :             qenergy = qenergy + dfilter(i)*theta**2
     144          152 :             fq(i) = -dfilter(i)*theta
     145              :          END DO
     146           38 :          qenergy = 0.25_dp*cell%deth/twopi*qenergy
     147          152 :          DO i = 1, SIZE(qforce, 2)
     148          494 :             qforce(1:3, i) = fq(1:3)*qforce(1:3, i)
     149              :          END DO
     150              :       ELSE
     151              :          ! E = -omega*E*P
     152          312 :          qenergy = SUM(fieldpol*dipole)
     153          312 :          DO i = 1, SIZE(qforce, 2)
     154         1014 :             qforce(1:3, i) = fieldpol(1:3)*qforce(1:3, i)
     155              :          END DO
     156              :       END IF
     157              : 
     158          116 :       IF (virial) THEN
     159            6 :          DO iparticle_kind = 1, SIZE(atomic_kind_set)
     160            4 :             atomic_kind => atomic_kind_set(iparticle_kind)
     161            4 :             CALL get_atomic_kind(atomic_kind=atomic_kind, atom_list=atom_list)
     162           12 :             DO i = 1, SIZE(atom_list)
     163            6 :                ii = atom_list(i)
     164           24 :                ria = particle_set(ii)%r(:)
     165           24 :                ria = pbc(ria, cell)
     166           28 :                DO j = 1, 3
     167           78 :                   qpv(j, 1:3) = qpv(j, 1:3) + qforce(j, ii)*ria(1:3)
     168              :                END DO
     169              :             END DO
     170              :          END DO
     171              :          ! Stress tensor for constant D needs further investigation
     172            2 :          IF (efield%displacement) THEN
     173            0 :             CPABORT("Stress Tensor for constant D simulation is not working")
     174              :          END IF
     175              :       END IF
     176              : 
     177          116 :    END SUBROUTINE fist_efield_energy_force
     178              : ! **************************************************************************************************
     179              : !> \brief Evaluates the Dipole of a classical charge distribution(point-like)
     180              : !>      possibly using the berry phase formalism
     181              : !> \param fist_env ...
     182              : !> \param print_section ...
     183              : !> \param atomic_kind_set ...
     184              : !> \param particle_set ...
     185              : !> \param cell ...
     186              : !> \param unit_nr ...
     187              : !> \param charges ...
     188              : !> \par History
     189              : !>      [01.2006] created
     190              : !>      [12.2007] tlaino - University of Zurich - debug and extended
     191              : !> \author Teodoro Laino
     192              : ! **************************************************************************************************
     193        42408 :    SUBROUTINE fist_dipole(fist_env, print_section, atomic_kind_set, particle_set, &
     194              :                           cell, unit_nr, charges)
     195              :       TYPE(fist_environment_type), POINTER               :: fist_env
     196              :       TYPE(section_vals_type), POINTER                   :: print_section
     197              :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     198              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     199              :       TYPE(cell_type), POINTER                           :: cell
     200              :       INTEGER, INTENT(IN)                                :: unit_nr
     201              :       REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER     :: charges
     202              : 
     203              :       CHARACTER(LEN=default_string_length)               :: description, dipole_type
     204              :       COMPLEX(KIND=dp)                                   :: dzeta, dzphase(3), zeta, zphase(3)
     205              :       COMPLEX(KIND=dp), DIMENSION(3)                     :: dggamma, ggamma
     206              :       INTEGER                                            :: i, iparticle_kind, j, reference
     207        21204 :       INTEGER, DIMENSION(:), POINTER                     :: atom_list
     208              :       LOGICAL                                            :: do_berry, use_charges
     209              :       REAL(KIND=dp) :: charge_tot, ci(3), dci(3), dipole(3), dipole_deriv(3), drcc(3), dria(3), &
     210              :          dtheta, gvec(3), q, rcc(3), ria(3), theta, tmp(3), via(3)
     211        21204 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: ref_point
     212              :       TYPE(atomic_kind_type), POINTER                    :: atomic_kind
     213              :       TYPE(cp_result_type), POINTER                      :: results
     214              : 
     215        21204 :       NULLIFY (atomic_kind)
     216              :       ! Reference point
     217        42408 :       reference = section_get_ival(print_section, keyword_name="DIPOLE%REFERENCE")
     218        21204 :       NULLIFY (ref_point)
     219        21204 :       description = '[DIPOLE]'
     220        21204 :       CALL section_vals_val_get(print_section, "DIPOLE%REF_POINT", r_vals=ref_point)
     221        21204 :       CALL section_vals_val_get(print_section, "DIPOLE%PERIODIC", l_val=do_berry)
     222        21204 :       use_charges = .FALSE.
     223        21204 :       IF (PRESENT(charges)) THEN
     224        21204 :          IF (ASSOCIATED(charges)) use_charges = .TRUE.
     225              :       END IF
     226              : 
     227        21204 :       CALL get_reference_point(rcc, drcc, fist_env=fist_env, reference=reference, ref_point=ref_point)
     228              : 
     229              :       ! Dipole deriv will be the derivative of the Dipole(dM/dt=\sum e_j v_j)
     230        21204 :       dipole_deriv = 0.0_dp
     231        21204 :       dipole = 0.0_dp
     232        21204 :       IF (do_berry) THEN
     233        21204 :          dipole_type = "periodic (Berry phase)"
     234        84816 :          rcc = pbc(rcc, cell)
     235        21204 :          charge_tot = 0._dp
     236        21204 :          IF (use_charges) THEN
     237         2644 :             charge_tot = SUM(charges)
     238              :          ELSE
     239      2235036 :             DO i = 1, SIZE(particle_set)
     240      2214460 :                atomic_kind => particle_set(i)%atomic_kind
     241      2214460 :                CALL get_atomic_kind(atomic_kind=atomic_kind, qeff=q)
     242      2235036 :                charge_tot = charge_tot + q
     243              :             END DO
     244              :          END IF
     245       339264 :          ria = twopi*MATMUL(cell%h_inv, rcc)
     246        84816 :          zphase = CMPLX(COS(ria), SIN(ria), dp)**charge_tot
     247              : 
     248       339264 :          dria = twopi*MATMUL(cell%h_inv, drcc)
     249        84816 :          dzphase = charge_tot*CMPLX(-SIN(ria), COS(ria), dp)**(charge_tot - 1.0_dp)*dria
     250              : 
     251        84816 :          ggamma = z_one
     252        21204 :          dggamma = z_zero
     253        80920 :          DO iparticle_kind = 1, SIZE(atomic_kind_set)
     254        59716 :             atomic_kind => atomic_kind_set(iparticle_kind)
     255        59716 :             CALL get_atomic_kind(atomic_kind=atomic_kind, qeff=q, atom_list=atom_list)
     256              : 
     257      2297396 :             DO i = 1, SIZE(atom_list)
     258      8865904 :                ria = particle_set(atom_list(i))%r(:)
     259      8865904 :                ria = pbc(ria, cell)
     260      8865904 :                via = particle_set(atom_list(i))%v(:)
     261      2216476 :                IF (use_charges) q = charges(atom_list(i))
     262      8925620 :                DO j = 1, 3
     263     26597712 :                   gvec = twopi*cell%h_inv(j, :)
     264     26597712 :                   theta = SUM(ria(:)*gvec(:))
     265     26597712 :                   dtheta = SUM(via(:)*gvec(:))
     266      6649428 :                   zeta = CMPLX(COS(theta), SIN(theta), KIND=dp)**(-q)
     267      6649428 :                   dzeta = -q*CMPLX(-SIN(theta), COS(theta), KIND=dp)**(-q - 1.0_dp)*dtheta
     268      6649428 :                   dggamma(j) = dggamma(j)*zeta + ggamma(j)*dzeta
     269      8865904 :                   ggamma(j) = ggamma(j)*zeta
     270              :                END DO
     271              :             END DO
     272              :          END DO
     273        84816 :          dggamma = dggamma*zphase + ggamma*dzphase
     274        84816 :          ggamma = ggamma*zphase
     275        84816 :          IF (ALL(REAL(ggamma, KIND=dp) /= 0.0_dp)) THEN
     276        84816 :             tmp = AIMAG(ggamma)/REAL(ggamma, KIND=dp)
     277        84816 :             ci = ATAN(tmp)
     278              :             dci = (1.0_dp/(1.0_dp + tmp**2))* &
     279        84816 :                   (AIMAG(dggamma)*REAL(ggamma, KIND=dp) - AIMAG(ggamma)*REAL(dggamma, KIND=dp))/(REAL(ggamma, KIND=dp))**2
     280              : 
     281       339264 :             dipole = MATMUL(cell%hmat, ci)/twopi
     282       339264 :             dipole_deriv = MATMUL(cell%hmat, dci)/twopi
     283              :          END IF
     284        21204 :          CALL fist_env_get(fist_env=fist_env, results=results)
     285        21204 :          CALL cp_results_erase(results, description)
     286        21204 :          CALL put_results(results, description, dipole)
     287              :       ELSE
     288            0 :          dipole_type = "non-periodic"
     289            0 :          DO i = 1, SIZE(particle_set)
     290            0 :             atomic_kind => particle_set(i)%atomic_kind
     291            0 :             ria = particle_set(i)%r(:) ! no pbc(particle_set(i)%r(:),cell) so that the total dipole
     292              :             ! is the sum of the molecular dipoles
     293            0 :             CALL get_atomic_kind(atomic_kind=atomic_kind, qeff=q)
     294            0 :             IF (use_charges) q = charges(i)
     295            0 :             dipole = dipole - q*(ria - rcc)
     296            0 :             dipole_deriv(:) = dipole_deriv(:) - q*(particle_set(i)%v(:) - drcc)
     297              :          END DO
     298            0 :          CALL fist_env_get(fist_env=fist_env, results=results)
     299            0 :          CALL cp_results_erase(results, description)
     300            0 :          CALL put_results(results, description, dipole)
     301              :       END IF
     302        21204 :       IF (unit_nr > 0) THEN
     303              :          WRITE (unit_nr, '(/,T2,A,T31,A50)') &
     304        10855 :             'MM_DIPOLE| Dipole type', ADJUSTR(TRIM(dipole_type))
     305              :          WRITE (unit_nr, '(T2,A,T30,3(1X,F16.8))') &
     306        10855 :             'MM_DIPOLE| Moment [a.u.]', dipole(1:3)
     307              :          WRITE (unit_nr, '(T2,A,T30,3(1X,F16.8))') &
     308        43420 :             'MM_DIPOLE| Moment [Debye]', dipole(1:3)*debye
     309              :          WRITE (unit_nr, '(T2,A,T30,3(1X,F16.8))') &
     310        10855 :             'MM_DIPOLE| Derivative [a.u.]', dipole_deriv(1:3)
     311              :       END IF
     312              : 
     313        21204 :    END SUBROUTINE fist_dipole
     314              : 
     315              : END MODULE fist_efield_methods
        

Generated by: LCOV version 2.0-1