LCOV - code coverage report
Current view: top level - src - qmmm_util.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:2fce0f8) Lines: 215 256 84.0 %
Date: 2024-12-21 06:28:57 Functions: 10 10 100.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \par History
      10             : !>      09.2004 created [tlaino]
      11             : !> \author Teodoro Laino
      12             : ! **************************************************************************************************
      13             : MODULE qmmm_util
      14             :    USE cell_types,                      ONLY: cell_type
      15             :    USE cp_log_handling,                 ONLY: cp_logger_get_default_io_unit
      16             :    USE cp_subsys_types,                 ONLY: cp_subsys_type
      17             :    USE fist_environment_types,          ONLY: fist_env_get
      18             :    USE force_env_types,                 ONLY: force_env_type,&
      19             :                                               use_qmmm,&
      20             :                                               use_qmmmx
      21             :    USE input_constants,                 ONLY: do_qmmm_wall_none,&
      22             :                                               do_qmmm_wall_quadratic,&
      23             :                                               do_qmmm_wall_reflective
      24             :    USE input_section_types,             ONLY: section_vals_get,&
      25             :                                               section_vals_get_subs_vals,&
      26             :                                               section_vals_type,&
      27             :                                               section_vals_val_get
      28             :    USE kinds,                           ONLY: dp
      29             :    USE mathconstants,                   ONLY: pi
      30             :    USE particle_methods,                ONLY: write_fist_particle_coordinates,&
      31             :                                               write_qs_particle_coordinates
      32             :    USE particle_types,                  ONLY: particle_type
      33             :    USE qmmm_types,                      ONLY: qmmm_env_type
      34             :    USE qs_energy_types,                 ONLY: qs_energy_type
      35             :    USE qs_environment_types,            ONLY: get_qs_env
      36             :    USE qs_kind_types,                   ONLY: qs_kind_type
      37             : #include "./base/base_uses.f90"
      38             : 
      39             :    IMPLICIT NONE
      40             :    PRIVATE
      41             : 
      42             :    LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .FALSE.
      43             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qmmm_util'
      44             :    PUBLIC :: apply_qmmm_walls_reflective, &
      45             :              apply_qmmm_walls, &
      46             :              apply_qmmm_translate, &
      47             :              apply_qmmm_wrap, &
      48             :              apply_qmmm_unwrap, &
      49             :              spherical_cutoff_factor
      50             : 
      51             : CONTAINS
      52             : 
      53             : ! **************************************************************************************************
      54             : !> \brief Apply QM quadratic walls in order to avoid QM atoms escaping from
      55             : !>      the QM Box
      56             : !> \param qmmm_env ...
      57             : !> \par History
      58             : !>      02.2008 created
      59             : !> \author Benjamin G Levine
      60             : ! **************************************************************************************************
      61       11406 :    SUBROUTINE apply_qmmm_walls(qmmm_env)
      62             :       TYPE(qmmm_env_type), POINTER                       :: qmmm_env
      63             : 
      64             :       INTEGER                                            :: iwall_type
      65             :       LOGICAL                                            :: do_qmmm_force_mixing, explicit
      66             :       TYPE(section_vals_type), POINTER                   :: qmmmx_section, walls_section
      67             : 
      68        3802 :       walls_section => section_vals_get_subs_vals(qmmm_env%qs_env%input, "QMMM%WALLS")
      69        3802 :       qmmmx_section => section_vals_get_subs_vals(qmmm_env%qs_env%input, "QMMM%FORCE_MIXING")
      70        3802 :       CALL section_vals_get(qmmmx_section, explicit=do_qmmm_force_mixing)
      71        3802 :       CALL section_vals_get(walls_section, explicit=explicit)
      72        3802 :       IF (explicit) THEN
      73         404 :          CALL section_vals_val_get(walls_section, "TYPE", i_val=iwall_type)
      74         202 :          SELECT CASE (iwall_type)
      75             :          CASE (do_qmmm_wall_quadratic)
      76         404 :             IF (do_qmmm_force_mixing) THEN
      77             :                CALL cp_warn(__LOCATION__, &
      78             :                             "Quadratic walls for QM/MM are not implemented (or useful), when "// &
      79           0 :                             "force mixing is active.  Skipping!")
      80             :             ELSE
      81         202 :                CALL apply_qmmm_walls_quadratic(qmmm_env, walls_section)
      82             :             END IF
      83             :          CASE (do_qmmm_wall_reflective)
      84             :             ! Do nothing.. reflective walls are applied directly in the integrator
      85             :          END SELECT
      86             :       END IF
      87             : 
      88        3802 :    END SUBROUTINE apply_qmmm_walls
      89             : 
      90             : ! **************************************************************************************************
      91             : !> \brief Apply reflective QM walls in order to avoid QM atoms escaping from
      92             : !>      the QM Box
      93             : !> \param force_env ...
      94             : !> \par History
      95             : !>      08.2007 created [tlaino] - Zurich University
      96             : !> \author Teodoro Laino
      97             : ! **************************************************************************************************
      98       42895 :    SUBROUTINE apply_qmmm_walls_reflective(force_env)
      99             :       TYPE(force_env_type), POINTER                      :: force_env
     100             : 
     101             :       INTEGER                                            :: ip, iwall_type, qm_index
     102       41577 :       INTEGER, DIMENSION(:), POINTER                     :: qm_atom_index
     103             :       LOGICAL                                            :: explicit, is_x(2), is_y(2), is_z(2)
     104             :       REAL(KIND=dp), DIMENSION(3)                        :: coord, qm_cell_diag, skin
     105       41577 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: list
     106             :       TYPE(cell_type), POINTER                           :: mm_cell, qm_cell
     107             :       TYPE(cp_subsys_type), POINTER                      :: subsys_mm, subsys_qm
     108       41577 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particles_mm
     109             :       TYPE(section_vals_type), POINTER                   :: walls_section
     110             : 
     111       41577 :       NULLIFY (subsys_mm, subsys_qm, qm_atom_index, particles_mm, qm_cell, mm_cell, &
     112       41577 :                walls_section)
     113             : 
     114       40307 :       IF (force_env%in_use /= use_qmmm .AND. force_env%in_use /= use_qmmmx) RETURN
     115             : 
     116        1318 :       walls_section => section_vals_get_subs_vals(force_env%root_section, "FORCE_EVAL%QMMM%WALLS")
     117        1318 :       CALL section_vals_get(walls_section, explicit=explicit)
     118        1318 :       IF (explicit) THEN
     119         400 :          NULLIFY (list)
     120         400 :          CALL section_vals_val_get(walls_section, "WALL_SKIN", r_vals=list)
     121         400 :          CALL section_vals_val_get(walls_section, "TYPE", i_val=iwall_type)
     122        1600 :          skin(:) = list(:)
     123             :       ELSE
     124             :          ![NB]
     125         918 :          iwall_type = do_qmmm_wall_reflective
     126         918 :          skin(:) = 0.0_dp
     127             :       END IF
     128             : 
     129        1318 :       IF (force_env%in_use == use_qmmmx) THEN
     130          48 :          IF (iwall_type /= do_qmmm_wall_none) &
     131             :             CALL cp_warn(__LOCATION__, &
     132             :                          "Reflective walls for QM/MM are not implemented (or useful) when "// &
     133          48 :                          "force mixing is active.  Skipping!")
     134          48 :          RETURN
     135             :       END IF
     136             : 
     137             :       ! from here on we can be sure that it's conventional QM/MM
     138        1270 :       CPASSERT(ASSOCIATED(force_env%qmmm_env))
     139             : 
     140        1270 :       CALL fist_env_get(force_env%qmmm_env%fist_env, cell=mm_cell, subsys=subsys_mm)
     141        1270 :       CALL get_qs_env(force_env%qmmm_env%qs_env, cell=qm_cell, cp_subsys=subsys_qm)
     142        1270 :       qm_atom_index => force_env%qmmm_env%qm%qm_atom_index
     143        1270 :       CPASSERT(ASSOCIATED(qm_atom_index))
     144             : 
     145             :       qm_cell_diag = (/qm_cell%hmat(1, 1), &
     146             :                        qm_cell%hmat(2, 2), &
     147        5080 :                        qm_cell%hmat(3, 3)/)
     148        1270 :       particles_mm => subsys_mm%particles%els
     149        7120 :       DO ip = 1, SIZE(qm_atom_index)
     150        5850 :          qm_index = qm_atom_index(ip)
     151       23400 :          coord = particles_mm(qm_index)%r
     152       48034 :          IF (ANY(coord < skin) .OR. ANY(coord > (qm_cell_diag - skin))) THEN
     153          12 :             IF (explicit) THEN
     154          12 :                IF (iwall_type == do_qmmm_wall_reflective) THEN
     155             :                   ! Apply Walls
     156           2 :                   is_x(1) = (coord(1) < skin(1))
     157           2 :                   is_x(2) = (coord(1) > (qm_cell_diag(1) - skin(1)))
     158           2 :                   is_y(1) = (coord(2) < skin(2))
     159           2 :                   is_y(2) = (coord(2) > (qm_cell_diag(2) - skin(2)))
     160           2 :                   is_z(1) = (coord(3) < skin(3))
     161           2 :                   is_z(2) = (coord(3) > (qm_cell_diag(3) - skin(3)))
     162           2 :                   IF (ANY(is_x)) THEN
     163             :                      ! X coordinate
     164           2 :                      IF (is_x(1)) THEN
     165           2 :                         particles_mm(qm_index)%v(1) = ABS(particles_mm(qm_index)%v(1))
     166           0 :                      ELSE IF (is_x(2)) THEN
     167           0 :                         particles_mm(qm_index)%v(1) = -ABS(particles_mm(qm_index)%v(1))
     168             :                      END IF
     169             :                   END IF
     170           6 :                   IF (ANY(is_y)) THEN
     171             :                      ! Y coordinate
     172           0 :                      IF (is_y(1)) THEN
     173           0 :                         particles_mm(qm_index)%v(2) = ABS(particles_mm(qm_index)%v(2))
     174           0 :                      ELSE IF (is_y(2)) THEN
     175           0 :                         particles_mm(qm_index)%v(2) = -ABS(particles_mm(qm_index)%v(2))
     176             :                      END IF
     177             :                   END IF
     178           6 :                   IF (ANY(is_z)) THEN
     179             :                      ! Z coordinate
     180           0 :                      IF (is_z(1)) THEN
     181           0 :                         particles_mm(qm_index)%v(3) = ABS(particles_mm(qm_index)%v(3))
     182           0 :                      ELSE IF (is_z(2)) THEN
     183           0 :                         particles_mm(qm_index)%v(3) = -ABS(particles_mm(qm_index)%v(3))
     184             :                      END IF
     185             :                   END IF
     186             :                END IF
     187             :             ELSE
     188             :                ! Otherwise print a warning and continue crossing cp2k's finger..
     189             :                CALL cp_warn(__LOCATION__, &
     190             :                             "One or few QM atoms are within the SKIN of the quantum box. Check your run "// &
     191             :                             "and you may possibly consider: the activation of the QMMM WALLS "// &
     192             :                             "around the QM box, switching ON the centering of the QM box or increase "// &
     193           0 :                             "the size of the QM cell. CP2K CONTINUE but results could be meaningless. ")
     194             :             END IF
     195             :          END IF
     196             :       END DO
     197             : 
     198       41577 :    END SUBROUTINE apply_qmmm_walls_reflective
     199             : 
     200             : ! **************************************************************************************************
     201             : !> \brief Apply QM quadratic walls in order to avoid QM atoms escaping from
     202             : !>      the QM Box
     203             : !> \param qmmm_env ...
     204             : !> \param walls_section ...
     205             : !> \par History
     206             : !>      02.2008 created
     207             : !> \author Benjamin G Levine
     208             : ! **************************************************************************************************
     209         404 :    SUBROUTINE apply_qmmm_walls_quadratic(qmmm_env, walls_section)
     210             :       TYPE(qmmm_env_type), POINTER                       :: qmmm_env
     211             :       TYPE(section_vals_type), POINTER                   :: walls_section
     212             : 
     213             :       INTEGER                                            :: ip, qm_index
     214         202 :       INTEGER, DIMENSION(:), POINTER                     :: qm_atom_index
     215             :       LOGICAL                                            :: is_x(2), is_y(2), is_z(2)
     216             :       REAL(KIND=dp)                                      :: k, wallenergy, wallforce
     217             :       REAL(KIND=dp), DIMENSION(3)                        :: coord, qm_cell_diag, skin
     218         202 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: list
     219             :       TYPE(cell_type), POINTER                           :: mm_cell, qm_cell
     220             :       TYPE(cp_subsys_type), POINTER                      :: subsys_mm, subsys_qm
     221         202 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particles_mm
     222             :       TYPE(qs_energy_type), POINTER                      :: energy
     223             : 
     224         202 :       NULLIFY (list)
     225         202 :       CALL section_vals_val_get(walls_section, "WALL_SKIN", r_vals=list)
     226         202 :       CALL section_vals_val_get(walls_section, "K", r_val=k)
     227         202 :       CPASSERT(ASSOCIATED(qmmm_env))
     228             : 
     229         202 :       CALL fist_env_get(qmmm_env%fist_env, cell=mm_cell, subsys=subsys_mm)
     230         202 :       CALL get_qs_env(qmmm_env%qs_env, cell=qm_cell, cp_subsys=subsys_qm)
     231             : 
     232         202 :       qm_atom_index => qmmm_env%qm%qm_atom_index
     233         202 :       CPASSERT(ASSOCIATED(qm_atom_index))
     234             : 
     235         808 :       skin(:) = list(:)
     236             : 
     237             :       qm_cell_diag = (/qm_cell%hmat(1, 1), &
     238             :                        qm_cell%hmat(2, 2), &
     239         808 :                        qm_cell%hmat(3, 3)/)
     240         202 :       particles_mm => subsys_mm%particles%els
     241         202 :       wallenergy = 0.0_dp
     242         808 :       DO ip = 1, SIZE(qm_atom_index)
     243         606 :          qm_index = qm_atom_index(ip)
     244        2424 :          coord = particles_mm(qm_index)%r
     245        5014 :          IF (ANY(coord < skin) .OR. ANY(coord > (qm_cell_diag - skin))) THEN
     246          12 :             is_x(1) = (coord(1) < skin(1))
     247          12 :             is_x(2) = (coord(1) > (qm_cell_diag(1) - skin(1)))
     248          12 :             is_y(1) = (coord(2) < skin(2))
     249          12 :             is_y(2) = (coord(2) > (qm_cell_diag(2) - skin(2)))
     250          12 :             is_z(1) = (coord(3) < skin(3))
     251          12 :             is_z(2) = (coord(3) > (qm_cell_diag(3) - skin(3)))
     252          12 :             IF (is_x(1)) THEN
     253          12 :                wallforce = 2.0_dp*k*(skin(1) - coord(1))
     254             :                particles_mm(qm_index)%f(1) = particles_mm(qm_index)%f(1) + &
     255          12 :                                              wallforce
     256          12 :                wallenergy = wallenergy + wallforce*(skin(1) - coord(1))*0.5_dp
     257             :             END IF
     258          12 :             IF (is_x(2)) THEN
     259           0 :                wallforce = 2.0_dp*k*((qm_cell_diag(1) - skin(1)) - coord(1))
     260             :                particles_mm(qm_index)%f(1) = particles_mm(qm_index)%f(1) + &
     261           0 :                                              wallforce
     262             :                wallenergy = wallenergy + wallforce*((qm_cell_diag(1) - skin(1)) - &
     263           0 :                                                     coord(1))*0.5_dp
     264             :             END IF
     265          12 :             IF (is_y(1)) THEN
     266           0 :                wallforce = 2.0_dp*k*(skin(2) - coord(2))
     267             :                particles_mm(qm_index)%f(2) = particles_mm(qm_index)%f(2) + &
     268           0 :                                              wallforce
     269           0 :                wallenergy = wallenergy + wallforce*(skin(2) - coord(2))*0.5_dp
     270             :             END IF
     271          12 :             IF (is_y(2)) THEN
     272           0 :                wallforce = 2.0_dp*k*((qm_cell_diag(2) - skin(2)) - coord(2))
     273             :                particles_mm(qm_index)%f(2) = particles_mm(qm_index)%f(2) + &
     274           0 :                                              wallforce
     275             :                wallenergy = wallenergy + wallforce*((qm_cell_diag(2) - skin(2)) - &
     276           0 :                                                     coord(2))*0.5_dp
     277             :             END IF
     278          12 :             IF (is_z(1)) THEN
     279           0 :                wallforce = 2.0_dp*k*(skin(3) - coord(3))
     280             :                particles_mm(qm_index)%f(3) = particles_mm(qm_index)%f(3) + &
     281           0 :                                              wallforce
     282           0 :                wallenergy = wallenergy + wallforce*(skin(3) - coord(3))*0.5_dp
     283             :             END IF
     284          12 :             IF (is_z(2)) THEN
     285           0 :                wallforce = 2.0_dp*k*((qm_cell_diag(3) - skin(3)) - coord(3))
     286             :                particles_mm(qm_index)%f(3) = particles_mm(qm_index)%f(3) + &
     287           0 :                                              wallforce
     288             :                wallenergy = wallenergy + wallforce*((qm_cell_diag(3) - skin(3)) - &
     289           0 :                                                     coord(3))*0.5_dp
     290             :             END IF
     291             :          END IF
     292             :       END DO
     293             : 
     294         202 :       CALL get_qs_env(qs_env=qmmm_env%qs_env, energy=energy)
     295         202 :       energy%total = energy%total + wallenergy
     296             : 
     297         202 :    END SUBROUTINE apply_qmmm_walls_quadratic
     298             : 
     299             : ! **************************************************************************************************
     300             : !> \brief wrap positions (with mm periodicity)
     301             : !> \param subsys_mm ...
     302             : !> \param mm_cell ...
     303             : !> \param subsys_qm ...
     304             : !> \param qm_atom_index ...
     305             : !> \param saved_pos ...
     306             : ! **************************************************************************************************
     307         104 :    SUBROUTINE apply_qmmm_wrap(subsys_mm, mm_cell, subsys_qm, qm_atom_index, saved_pos)
     308             :       TYPE(cp_subsys_type), POINTER                      :: subsys_mm
     309             :       TYPE(cell_type), POINTER                           :: mm_cell
     310             :       TYPE(cp_subsys_type), OPTIONAL, POINTER            :: subsys_qm
     311             :       INTEGER, DIMENSION(:), OPTIONAL, POINTER           :: qm_atom_index
     312             :       REAL(dp), ALLOCATABLE                              :: saved_pos(:, :)
     313             : 
     314             :       INTEGER                                            :: i_dim, ip
     315             :       REAL(dp)                                           :: r_lat(3)
     316             : 
     317         312 :       ALLOCATE (saved_pos(3, subsys_mm%particles%n_els))
     318      199676 :       DO ip = 1, subsys_mm%particles%n_els
     319      798288 :          saved_pos(1:3, ip) = subsys_mm%particles%els(ip)%r(1:3)
     320     2594436 :          r_lat = MATMUL(mm_cell%h_inv, subsys_mm%particles%els(ip)%r)
     321      798288 :          DO i_dim = 1, 3
     322      798288 :             IF (mm_cell%perd(i_dim) /= 1) THEN
     323           0 :                r_lat(i_dim) = 0.0_dp
     324             :             END IF
     325             :          END DO
     326     3791972 :          subsys_mm%particles%els(ip)%r = subsys_mm%particles%els(ip)%r - MATMUL(mm_cell%hmat, FLOOR(r_lat))
     327             :       END DO
     328             : 
     329         104 :       IF (PRESENT(subsys_qm) .AND. PRESENT(qm_atom_index)) THEN
     330        2444 :          DO ip = 1, SIZE(qm_atom_index)
     331       18824 :             subsys_qm%particles%els(ip)%r = subsys_mm%particles%els(qm_atom_index(ip))%r
     332             :          END DO
     333             :       END IF
     334         104 :    END SUBROUTINE apply_qmmm_wrap
     335             : 
     336             : ! **************************************************************************************************
     337             : !> \brief ...
     338             : !> \param subsys_mm ...
     339             : !> \param subsys_qm ...
     340             : !> \param qm_atom_index ...
     341             : !> \param saved_pos ...
     342             : ! **************************************************************************************************
     343         104 :    SUBROUTINE apply_qmmm_unwrap(subsys_mm, subsys_qm, qm_atom_index, saved_pos)
     344             :       TYPE(cp_subsys_type), POINTER                      :: subsys_mm
     345             :       TYPE(cp_subsys_type), OPTIONAL, POINTER            :: subsys_qm
     346             :       INTEGER, DIMENSION(:), OPTIONAL, POINTER           :: qm_atom_index
     347             :       REAL(dp), ALLOCATABLE                              :: saved_pos(:, :)
     348             : 
     349             :       INTEGER                                            :: ip
     350             : 
     351      199676 :       DO ip = 1, subsys_mm%particles%n_els
     352      798392 :          subsys_mm%particles%els(ip)%r(1:3) = saved_pos(1:3, ip)
     353             :       END DO
     354             : 
     355         104 :       IF (PRESENT(subsys_qm) .AND. PRESENT(qm_atom_index)) THEN
     356        2444 :          DO ip = 1, SIZE(qm_atom_index)
     357       18824 :             subsys_qm%particles%els(ip)%r = subsys_mm%particles%els(qm_atom_index(ip))%r
     358             :          END DO
     359             :       END IF
     360             : 
     361         104 :       DEALLOCATE (saved_pos)
     362         104 :    END SUBROUTINE apply_qmmm_unwrap
     363             : 
     364             : ! **************************************************************************************************
     365             : !> \brief Apply translation to the full system in order to center the QM
     366             : !>      system into the QM box
     367             : !> \param qmmm_env ...
     368             : !> \par History
     369             : !>      08.2007 created [tlaino] - Zurich University
     370             : !> \author Teodoro Laino
     371             : ! **************************************************************************************************
     372        3914 :    SUBROUTINE apply_qmmm_translate(qmmm_env)
     373             :       TYPE(qmmm_env_type), POINTER                       :: qmmm_env
     374             : 
     375             :       INTEGER                                            :: bigger_ip, i_dim, ip, max_ip, min_ip, &
     376             :                                                             smaller_ip, tmp_ip, unit_nr
     377             :       INTEGER, DIMENSION(:), POINTER                     :: qm_atom_index
     378        3914 :       LOGICAL, ALLOCATABLE                               :: avoid(:)
     379             :       REAL(DP) :: bigger_lat_dv, center_p(3), lat_dv, lat_dv3(3), lat_min(3), lat_p(3), &
     380             :          max_coord_lat(3), min_coord_lat(3), smaller_lat_dv
     381        3914 :       REAL(DP), POINTER                                  :: charges(:)
     382             :       REAL(KIND=dp), DIMENSION(3)                        :: max_coord, min_coord, transl_v
     383             :       TYPE(cell_type), POINTER                           :: mm_cell, qm_cell
     384             :       TYPE(cp_subsys_type), POINTER                      :: subsys_mm, subsys_qm
     385        3914 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particles_mm, particles_qm
     386        3914 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     387             :       TYPE(section_vals_type), POINTER                   :: subsys_section
     388             : 
     389        3914 :       NULLIFY (subsys_mm, subsys_qm, qm_atom_index, particles_mm, particles_qm, &
     390        3914 :                subsys_section, qm_cell, mm_cell, qs_kind_set)
     391             : 
     392           0 :       CPASSERT(ASSOCIATED(qmmm_env))
     393             : 
     394        3914 :       CALL fist_env_get(qmmm_env%fist_env, cell=mm_cell, subsys=subsys_mm)
     395        3914 :       CALL get_qs_env(qmmm_env%qs_env, cell=qm_cell, cp_subsys=subsys_qm)
     396        3914 :       qm_atom_index => qmmm_env%qm%qm_atom_index
     397        3914 :       CPASSERT(ASSOCIATED(qm_atom_index))
     398             : 
     399        3914 :       particles_qm => subsys_qm%particles%els
     400        3914 :       particles_mm => subsys_mm%particles%els
     401        3914 :       IF (.NOT. qmmm_env%qm%center_qm_subsys0) qmmm_env%qm%do_translate = .FALSE.
     402        3914 :       IF (qmmm_env%qm%do_translate) THEN
     403         972 :          IF (.NOT. qmmm_env%qm%center_qm_subsys_pbc_aware) THEN
     404             :             ! naive coordinate based min-max
     405        3792 :             min_coord = HUGE(0.0_dp)
     406        3792 :             max_coord = -HUGE(0.0_dp)
     407        7996 :             DO ip = 1, SIZE(qm_atom_index)
     408       28192 :                min_coord = MIN(min_coord, particles_mm(qm_atom_index(ip))%r)
     409       29140 :                max_coord = MAX(max_coord, particles_mm(qm_atom_index(ip))%r)
     410             :             END DO
     411             :          ELSE
     412             :             !! periodic based min max (uses complex number based mean)
     413          24 :             center_p = qmmm_pbc_aware_mean(particles_mm, mm_cell, qm_atom_index)
     414          72 :             ALLOCATE (avoid(SIZE(qm_atom_index)))
     415          96 :             DO i_dim = 1, 3
     416          96 :                IF (mm_cell%perd(i_dim) /= 1) THEN
     417             :                   ! find absolute min and max positions (along i_dim direction) in lattice coordinates
     418           0 :                   min_coord_lat(i_dim) = HUGE(0.0_dp)
     419           0 :                   max_coord_lat(i_dim) = -HUGE(0.0_dp)
     420           0 :                   DO ip = 1, SIZE(qm_atom_index)
     421           0 :                      lat_p = MATMUL(mm_cell%h_inv, particles_mm(qm_atom_index(ip))%r)
     422           0 :                      min_coord_lat(i_dim) = MIN(lat_p(i_dim), min_coord_lat(i_dim))
     423           0 :                      max_coord_lat(i_dim) = MAX(lat_p(i_dim), max_coord_lat(i_dim))
     424             :                   END DO
     425             :                ELSE
     426             :                   ! find min_ip closest to (pbc-aware) mean pos
     427         828 :                   avoid = .FALSE.
     428          72 :                   min_ip = qmmm_find_closest(particles_mm, mm_cell, qm_atom_index, avoid, center_p, i_dim, 0)
     429          72 :                   avoid(min_ip) = .TRUE.
     430             :                   ! find max_ip closest to min_ip
     431             :                   max_ip = qmmm_find_closest(particles_mm, mm_cell, qm_atom_index, avoid, &
     432          72 :                                              particles_mm(qm_atom_index(min_ip))%r, i_dim, 0, lat_dv)
     433          72 :                   avoid(max_ip) = .TRUE.
     434             :                   ! switch min and max if necessary
     435          72 :                   IF (lat_dv < 0.0) THEN
     436           0 :                      tmp_ip = min_ip
     437           0 :                      min_ip = max_ip
     438           0 :                      max_ip = tmp_ip
     439             :                   END IF
     440             :                   ! loop over all other atoms
     441        2726 :                   DO WHILE (.NOT. ALL(avoid))
     442             :                      ! find smaller below min, bigger after max
     443             :                      smaller_ip = qmmm_find_closest(particles_mm, mm_cell, qm_atom_index, &
     444         612 :                                                     avoid, particles_mm(qm_atom_index(min_ip))%r, i_dim, -1, smaller_lat_dv)
     445             :                      bigger_ip = qmmm_find_closest(particles_mm, mm_cell, qm_atom_index, &
     446         612 :                                                    avoid, particles_mm(qm_atom_index(max_ip))%r, i_dim, 1, bigger_lat_dv)
     447             :                      ! move min or max, not both
     448         684 :                      IF (ABS(smaller_lat_dv) < ABS(bigger_lat_dv)) THEN
     449         180 :                         min_ip = smaller_ip
     450         180 :                         avoid(min_ip) = .TRUE.
     451             :                      ELSE
     452         432 :                         max_ip = bigger_ip
     453         432 :                         avoid(max_ip) = .TRUE.
     454             :                      END IF
     455             :                   END DO
     456             :                   ! find min and max coordinates in lattice positions (i_dim ! only)
     457          72 :                   lat_dv3 = qmmm_lat_dv(mm_cell, particles_mm(qm_atom_index(min_ip))%r, particles_mm(qm_atom_index(max_ip))%r)
     458          72 :                   IF (lat_dv3(i_dim) < 0.0_dp) lat_dv3(i_dim) = lat_dv3(i_dim) + 1.0_dp
     459         936 :                   lat_min = MATMUL(mm_cell%h_inv, particles_mm(qm_atom_index(min_ip))%r)
     460          72 :                   min_coord_lat(i_dim) = lat_min(i_dim)
     461          72 :                   max_coord_lat(i_dim) = lat_min(i_dim) + lat_dv3(i_dim)
     462             :                END IF ! periodic
     463             :             END DO ! i_dim
     464             :             ! min and max coordinates from lattice positions to Cartesian
     465         312 :             min_coord = MATMUL(mm_cell%hmat, min_coord_lat)
     466         312 :             max_coord = MATMUL(mm_cell%hmat, max_coord_lat)
     467          24 :             DEALLOCATE (avoid)
     468             :          END IF ! pbc aware center
     469        3888 :          transl_v = (max_coord + min_coord)/2.0_dp
     470             : 
     471             :          !
     472             :          ! The first time we always translate all the system in order
     473             :          ! to centre the QM system in the box.
     474             :          !
     475       12636 :          transl_v(:) = transl_v(:) - SUM(qm_cell%hmat, 2)/2.0_dp
     476             : 
     477        3726 :          IF (ANY(qmmm_env%qm%utrasl /= 1.0_dp)) THEN
     478             :             transl_v = REAL(FLOOR(transl_v/qmmm_env%qm%utrasl), KIND=dp)* &
     479         216 :                        qmmm_env%qm%utrasl
     480             :          END IF
     481        3888 :          qmmm_env%qm%transl_v = qmmm_env%qm%transl_v + transl_v
     482         972 :          particles_mm => subsys_mm%particles%els
     483     1150822 :          DO ip = 1, subsys_mm%particles%n_els
     484     4600372 :             particles_mm(ip)%r = particles_mm(ip)%r - transl_v
     485             :          END DO
     486         972 :          IF (qmmm_env%qm%added_shells%num_mm_atoms .GT. 0) THEN
     487           0 :             DO ip = 1, qmmm_env%qm%added_shells%num_mm_atoms
     488           0 :                qmmm_env%qm%added_shells%added_particles(ip)%r = qmmm_env%qm%added_shells%added_particles(ip)%r - transl_v
     489           0 :                qmmm_env%qm%added_shells%added_cores(ip)%r = qmmm_env%qm%added_shells%added_cores(ip)%r - transl_v
     490             :             END DO
     491             :          END IF
     492         972 :          unit_nr = cp_logger_get_default_io_unit()
     493         972 :          IF (unit_nr > 0) WRITE (unit=unit_nr, fmt='(/1X,A)') &
     494         490 :             " Translating the system in order to center the QM fragment in the QM box."
     495         972 :          IF (.NOT. qmmm_env%qm%center_qm_subsys) qmmm_env%qm%do_translate = .FALSE.
     496             :       END IF
     497        3914 :       particles_mm => subsys_mm%particles%els
     498       22520 :       DO ip = 1, SIZE(qm_atom_index)
     499      152762 :          particles_qm(ip)%r = particles_mm(qm_atom_index(ip))%r
     500             :       END DO
     501             : 
     502        3914 :       subsys_section => section_vals_get_subs_vals(qmmm_env%qs_env%input, "SUBSYS")
     503             : 
     504        3914 :       CALL get_qs_env(qs_env=qmmm_env%qs_env, qs_kind_set=qs_kind_set)
     505        3914 :       CALL write_qs_particle_coordinates(particles_qm, qs_kind_set, subsys_section, "QM/MM first QM, then MM (0 charges)")
     506       11742 :       ALLOCATE (charges(SIZE(particles_mm)))
     507     1377072 :       charges = 0.0_dp
     508        3914 :       CALL write_fist_particle_coordinates(particles_mm, subsys_section, charges)
     509        3914 :       DEALLOCATE (charges)
     510             : 
     511        7828 :    END SUBROUTINE apply_qmmm_translate
     512             : 
     513             : ! **************************************************************************************************
     514             : !> \brief pbc-aware mean QM atom position
     515             : !> \param particles_mm ...
     516             : !> \param mm_cell ...
     517             : !> \param qm_atom_index ...
     518             : !> \return ...
     519             : ! **************************************************************************************************
     520          24 :    FUNCTION qmmm_pbc_aware_mean(particles_mm, mm_cell, qm_atom_index)
     521             :       TYPE(particle_type), DIMENSION(:), POINTER         :: particles_mm
     522             :       TYPE(cell_type), POINTER                           :: mm_cell
     523             :       INTEGER, DIMENSION(:), POINTER                     :: qm_atom_index
     524             :       REAL(dp)                                           :: qmmm_pbc_aware_mean(3)
     525             : 
     526             :       COMPLEX(dp)                                        :: mean_z(3)
     527             :       INTEGER                                            :: ip
     528             : 
     529          24 :       mean_z = 0.0_dp
     530         276 :       DO ip = 1, SIZE(qm_atom_index)
     531             :          mean_z = mean_z + EXP(CMPLX(0.0_dp, 1.0_dp, KIND=dp)*2.0*pi* &
     532        4056 :                                MATMUL(mm_cell%h_inv, particles_mm(qm_atom_index(ip))%r))
     533             :       END DO
     534          96 :       mean_z = mean_z/ABS(mean_z)
     535             :       qmmm_pbc_aware_mean = MATMUL(mm_cell%hmat, &
     536         456 :                                    REAL(LOG(mean_z)/(CMPLX(0.0_dp, 1.0_dp, KIND=dp)*2.0_dp*pi), dp))
     537             :    END FUNCTION
     538             : 
     539             : ! **************************************************************************************************
     540             : !> \brief minimum image lattice coordinates difference vector
     541             : !> \param mm_cell ...
     542             : !> \param p1 ...
     543             : !> \param p2 ...
     544             : !> \return ...
     545             : ! **************************************************************************************************
     546        7488 :    FUNCTION qmmm_lat_dv(mm_cell, p1, p2)
     547             :       TYPE(cell_type), POINTER                           :: mm_cell
     548             :       REAL(dp)                                           :: p1(3), p2(3), qmmm_lat_dv(3)
     549             : 
     550             :       REAL(dp)                                           :: lat_v1(3), lat_v2(3)
     551             : 
     552       97344 :       lat_v1 = MATMUL(mm_cell%h_inv, p1)
     553       97344 :       lat_v2 = MATMUL(mm_cell%h_inv, p2)
     554             : 
     555       29952 :       qmmm_lat_dv = lat_v2 - lat_v1
     556       29952 :       qmmm_lat_dv = qmmm_lat_dv - FLOOR(qmmm_lat_dv)
     557             :    END FUNCTION qmmm_lat_dv
     558             : 
     559             : ! **************************************************************************************************
     560             : !> \brief find closest QM particle, in position/negative direction
     561             : !>        if dir is 1 or -1, respectively
     562             : !> \param particles_mm ...
     563             : !> \param mm_cell ...
     564             : !> \param qm_atom_index ...
     565             : !> \param avoid ...
     566             : !> \param p ...
     567             : !> \param i_dim ...
     568             : !> \param dir ...
     569             : !> \param closest_dv ...
     570             : !> \return ...
     571             : ! **************************************************************************************************
     572        1368 :    FUNCTION qmmm_find_closest(particles_mm, mm_cell, qm_atom_index, avoid, p, i_dim, dir, closest_dv) RESULT(closest_ip)
     573             :       TYPE(particle_type), DIMENSION(:), POINTER         :: particles_mm
     574             :       TYPE(cell_type), POINTER                           :: mm_cell
     575             :       INTEGER, DIMENSION(:), POINTER                     :: qm_atom_index
     576             :       LOGICAL                                            :: avoid(:)
     577             :       REAL(dp)                                           :: p(3)
     578             :       INTEGER                                            :: i_dim, dir
     579             :       REAL(dp), OPTIONAL                                 :: closest_dv
     580             :       INTEGER                                            :: closest_ip
     581             : 
     582             :       INTEGER                                            :: ip, shift
     583             :       REAL(dp)                                           :: lat_dv3(3), lat_dv_shifted, my_closest_dv
     584             : 
     585        1368 :       closest_ip = -1
     586        1368 :       my_closest_dv = HUGE(0.0)
     587       16056 :       DO ip = 1, SIZE(qm_atom_index)
     588       14688 :          IF (avoid(ip)) CYCLE
     589        7416 :          lat_dv3 = qmmm_lat_dv(mm_cell, p, particles_mm(qm_atom_index(ip))%r)
     590       31032 :          DO shift = -1, 1
     591       22248 :             lat_dv_shifted = lat_dv3(i_dim) + shift*1.0_dp
     592       36936 :             IF (ABS(lat_dv_shifted) < ABS(my_closest_dv) .AND. (dir*lat_dv_shifted >= 0.0)) THEN
     593        2330 :                my_closest_dv = lat_dv_shifted
     594        2330 :                closest_ip = ip
     595             :             END IF
     596             :          END DO
     597             :       END DO
     598             : 
     599        1368 :       IF (PRESENT(closest_dv)) THEN
     600        1296 :          closest_dv = my_closest_dv
     601             :       END IF
     602             : 
     603        1368 :    END FUNCTION qmmm_find_closest
     604             : 
     605             : ! **************************************************************************************************
     606             : !> \brief Computes a spherical cutoff factor for the QMMM interactions
     607             : !> \param spherical_cutoff ...
     608             : !> \param rij ...
     609             : !> \param factor ...
     610             : !> \par History
     611             : !>      08.2008 created
     612             : !> \author Teodoro Laino
     613             : ! **************************************************************************************************
     614     1845816 :    SUBROUTINE spherical_cutoff_factor(spherical_cutoff, rij, factor)
     615             :       REAL(KIND=dp), DIMENSION(2), INTENT(IN)            :: spherical_cutoff
     616             :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: rij
     617             :       REAL(KIND=dp), INTENT(OUT)                         :: factor
     618             : 
     619             :       REAL(KIND=dp)                                      :: r, r0
     620             : 
     621     7383264 :       r = SQRT(DOT_PRODUCT(rij, rij))
     622     1845816 :       r0 = spherical_cutoff(1) - 20.0_dp*spherical_cutoff(2)
     623     1845816 :       factor = 0.5_dp*(1.0_dp - TANH((r - r0)/spherical_cutoff(2)))
     624             : 
     625     1845816 :    END SUBROUTINE spherical_cutoff_factor
     626             : 
     627             : END MODULE qmmm_util

Generated by: LCOV version 1.15