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