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 : !> \brief Module with utility for Nudged Elastic Band Calculation
10 : !> \note
11 : !> Numerical accuracy for parallel runs:
12 : !> Each replica starts the SCF run from the one optimized
13 : !> in a previous run. It may happen then energies and derivatives
14 : !> of a serial run and a parallel run could be slightly different
15 : !> 'cause of a different starting density matrix.
16 : !> Exact results are obtained using:
17 : !> EXTRAPOLATION USE_GUESS in QS section (Teo 09.2006)
18 : !> \author Teodoro Laino 10.2006
19 : ! **************************************************************************************************
20 : MODULE neb_utils
21 : USE bibliography, ONLY: E2002,&
22 : Elber1987,&
23 : Jonsson1998,&
24 : Jonsson2000_1,&
25 : Jonsson2000_2,&
26 : Wales2004,&
27 : cite_reference
28 : USE colvar_utils, ONLY: eval_colvar,&
29 : get_clv_force,&
30 : set_colvars_target
31 : USE cp_log_handling, ONLY: cp_get_default_logger,&
32 : cp_logger_type
33 : USE cp_output_handling, ONLY: cp_print_key_finished_output,&
34 : cp_print_key_unit_nr
35 : USE cp_parser_methods, ONLY: parser_get_next_line,&
36 : parser_get_object
37 : USE cp_parser_types, ONLY: cp_parser_type,&
38 : parser_create,&
39 : parser_release
40 : USE f77_interface, ONLY: f_env_add_defaults,&
41 : f_env_rm_defaults,&
42 : f_env_type,&
43 : get_energy,&
44 : get_force,&
45 : get_pos,&
46 : set_pos
47 : USE force_env_methods, ONLY: force_env_calc_energy_force
48 : USE force_env_types, ONLY: force_env_get
49 : USE geo_opt, ONLY: cp_geo_opt
50 : USE global_types, ONLY: global_environment_type
51 : USE input_constants, ONLY: &
52 : band_diis_opt, do_b_neb, do_band_cartesian, do_ci_neb, do_d_neb, do_eb, do_it_neb, do_sm, &
53 : pot_neb_fe, pot_neb_full, pot_neb_me
54 : USE input_cp2k_check, ONLY: remove_restart_info
55 : USE input_section_types, ONLY: section_vals_get,&
56 : section_vals_get_subs_vals,&
57 : section_vals_type,&
58 : section_vals_val_get
59 : USE kinds, ONLY: default_path_length,&
60 : default_string_length,&
61 : dp
62 : USE md_run, ONLY: qs_mol_dyn
63 : USE message_passing, ONLY: mp_para_env_type
64 : USE neb_io, ONLY: dump_replica_coordinates,&
65 : handle_band_file_names
66 : USE neb_md_utils, ONLY: neb_initialize_velocity
67 : USE neb_types, ONLY: neb_type,&
68 : neb_var_type
69 : USE particle_types, ONLY: particle_type
70 : USE physcon, ONLY: bohr
71 : USE replica_methods, ONLY: rep_env_calc_e_f
72 : USE replica_types, ONLY: rep_env_sync,&
73 : replica_env_type
74 : USE rmsd, ONLY: rmsd3
75 : #include "../base/base_uses.f90"
76 :
77 : IMPLICIT NONE
78 : PRIVATE
79 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'neb_utils'
80 : LOGICAL, PARAMETER, PRIVATE :: debug_this_module = .FALSE.
81 :
82 : PUBLIC :: build_replica_coords, &
83 : neb_calc_energy_forces, &
84 : reorient_images, &
85 : reparametrize_images, &
86 : check_convergence
87 :
88 : CONTAINS
89 :
90 : ! **************************************************************************************************
91 : !> \brief Computes the distance between two replica
92 : !> \param particle_set ...
93 : !> \param coords ...
94 : !> \param i0 ...
95 : !> \param i ...
96 : !> \param distance ...
97 : !> \param iw ...
98 : !> \param rotate ...
99 : !> \author Teodoro Laino 09.2006
100 : ! **************************************************************************************************
101 13686 : SUBROUTINE neb_replica_distance(particle_set, coords, i0, i, distance, iw, rotate)
102 : TYPE(particle_type), DIMENSION(:), OPTIONAL, &
103 : POINTER :: particle_set
104 : TYPE(neb_var_type), POINTER :: coords
105 : INTEGER, INTENT(IN) :: i0, i
106 : REAL(KIND=dp), INTENT(OUT) :: distance
107 : INTEGER, INTENT(IN) :: iw
108 : LOGICAL, INTENT(IN), OPTIONAL :: rotate
109 :
110 : LOGICAL :: my_rotate
111 :
112 13686 : my_rotate = .FALSE.
113 13686 : IF (PRESENT(rotate)) my_rotate = rotate
114 : ! The rotation of the replica is enabled exclusively when working in
115 : ! cartesian coordinates
116 13686 : IF (my_rotate .AND. (coords%in_use == do_band_cartesian)) THEN
117 444 : CPASSERT(PRESENT(particle_set))
118 : CALL rmsd3(particle_set, coords%xyz(:, i), coords%xyz(:, i0), &
119 444 : iw, rotate=my_rotate)
120 : END IF
121 : distance = SQRT(DOT_PRODUCT(coords%wrk(:, i) - coords%wrk(:, i0), &
122 1853878 : coords%wrk(:, i) - coords%wrk(:, i0)))
123 :
124 13686 : END SUBROUTINE neb_replica_distance
125 :
126 : ! **************************************************************************************************
127 : !> \brief Constructs or Read the coordinates for all replica
128 : !> \param neb_section ...
129 : !> \param particle_set ...
130 : !> \param coords ...
131 : !> \param vels ...
132 : !> \param neb_env ...
133 : !> \param iw ...
134 : !> \param globenv ...
135 : !> \param para_env ...
136 : !> \author Teodoro Laino 09.2006
137 : ! **************************************************************************************************
138 34 : SUBROUTINE build_replica_coords(neb_section, particle_set, &
139 : coords, vels, neb_env, iw, globenv, para_env)
140 : TYPE(section_vals_type), POINTER :: neb_section
141 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
142 : TYPE(neb_var_type), POINTER :: coords, vels
143 : TYPE(neb_type), POINTER :: neb_env
144 : INTEGER, INTENT(IN) :: iw
145 : TYPE(global_environment_type), POINTER :: globenv
146 : TYPE(mp_para_env_type), POINTER :: para_env
147 :
148 : CHARACTER(len=*), PARAMETER :: routineN = 'build_replica_coords'
149 :
150 : CHARACTER(LEN=default_path_length) :: filename
151 : INTEGER :: handle, i_rep, iatom, ic, input_nr_replica, is, ivar, j, jtarg, n_rep, natom, &
152 : neb_nr_replica, nr_replica_to_interpolate, nval, nvar, shell_index
153 : LOGICAL :: check, explicit, skip_vel_section
154 34 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: distance
155 : REAL(KIND=dp), DIMENSION(3) :: r
156 34 : REAL(KIND=dp), DIMENSION(:), POINTER :: initial_colvars, rptr
157 : TYPE(section_vals_type), POINTER :: coord_section, replica_section, &
158 : vel_section
159 :
160 34 : CALL timeset(routineN, handle)
161 34 : CPASSERT(ASSOCIATED(coords))
162 34 : CPASSERT(ASSOCIATED(vels))
163 34 : neb_nr_replica = neb_env%number_of_replica
164 34 : replica_section => section_vals_get_subs_vals(neb_section, "REPLICA")
165 34 : CALL section_vals_get(replica_section, n_repetition=input_nr_replica)
166 : ! Calculation is aborted if input replicas are more then the requested ones for the BAND..
167 34 : CPASSERT(input_nr_replica <= neb_nr_replica)
168 : ! Read in replicas coordinates
169 34 : skip_vel_section = (input_nr_replica /= neb_nr_replica)
170 34 : IF ((iw > 0) .AND. skip_vel_section) THEN
171 2 : WRITE (iw, '(T2,A)') 'NEB| The number of replica in the input is different from the number', &
172 2 : 'NEB| of replica requested for NEB. More Replica will be interpolated.', &
173 4 : 'NEB| Therefore the possibly provided velocities will not be read.'
174 : END IF
175 : ! Further check on velocity section...
176 194 : DO i_rep = 1, input_nr_replica
177 : vel_section => section_vals_get_subs_vals(replica_section, "VELOCITY", &
178 160 : i_rep_section=i_rep)
179 160 : CALL section_vals_get(vel_section, explicit=explicit)
180 222 : skip_vel_section = skip_vel_section .OR. (.NOT. explicit)
181 : END DO
182 : ! Setup cartesian coordinates and COLVAR (if requested)
183 41772 : coords%xyz(:, :) = 0.0_dp
184 194 : DO i_rep = 1, input_nr_replica
185 : coord_section => section_vals_get_subs_vals(replica_section, "COORD", &
186 160 : i_rep_section=i_rep)
187 160 : CALL section_vals_get(coord_section, explicit=explicit)
188 : ! Cartesian Coordinates
189 160 : IF (explicit) THEN
190 : CALL section_vals_val_get(coord_section, "_DEFAULT_KEYWORD_", &
191 32 : n_rep_val=natom)
192 32 : CPASSERT((natom == SIZE(particle_set)))
193 1512 : DO iatom = 1, natom
194 : CALL section_vals_val_get(coord_section, "_DEFAULT_KEYWORD_", &
195 1480 : i_rep_val=iatom, r_vals=rptr)
196 1480 : ic = 3*(iatom - 1)
197 10360 : coords%xyz(ic + 1:ic + 3, i_rep) = rptr(1:3)*bohr
198 : ! Initially core and shell positions are set to the atomic positions
199 1480 : shell_index = particle_set(iatom)%shell_index
200 1512 : IF (shell_index /= 0) THEN
201 1140 : is = 3*(natom + shell_index - 1)
202 7980 : coords%xyz(is + 1:is + 3, i_rep) = coords%xyz(ic + 1:ic + 3, i_rep)
203 : END IF
204 : END DO
205 : ELSE
206 128 : BLOCK
207 : LOGICAL :: my_end
208 : CHARACTER(LEN=default_string_length) :: dummy_char
209 : TYPE(cp_parser_type) :: parser
210 : CALL section_vals_val_get(replica_section, "COORD_FILE_NAME", &
211 128 : i_rep_section=i_rep, c_val=filename)
212 128 : CPASSERT(TRIM(filename) /= "")
213 128 : CALL parser_create(parser, filename, para_env=para_env, parse_white_lines=.TRUE.)
214 128 : CALL parser_get_next_line(parser, 1)
215 : ! Start parser
216 128 : CALL parser_get_object(parser, natom)
217 128 : CPASSERT((natom == SIZE(particle_set)))
218 128 : CALL parser_get_next_line(parser, 1)
219 9774 : DO iatom = 1, natom
220 : ! Atom coordinates
221 9646 : CALL parser_get_next_line(parser, 1, at_end=my_end)
222 9646 : IF (my_end) &
223 : CALL cp_abort(__LOCATION__, &
224 : "Number of lines in XYZ format not equal to the number of atoms."// &
225 : " Error in XYZ format for REPLICA coordinates. Very probably the"// &
226 0 : " line with title is missing or is empty. Please check the XYZ file and rerun your job!")
227 9646 : READ (parser%input_line, *) dummy_char, r(1:3)
228 9646 : ic = 3*(iatom - 1)
229 38584 : coords%xyz(ic + 1:ic + 3, i_rep) = r(1:3)*bohr
230 : ! Initially core and shell positions are set to the atomic positions
231 9646 : shell_index = particle_set(iatom)%shell_index
232 9774 : IF (shell_index /= 0) THEN
233 0 : is = 3*(natom + shell_index - 1)
234 0 : coords%xyz(is + 1:is + 3, i_rep) = coords%xyz(ic + 1:ic + 3, i_rep)
235 : END IF
236 : END DO
237 512 : CALL parser_release(parser)
238 : END BLOCK
239 : END IF
240 : ! Collective Variables
241 160 : IF (neb_env%use_colvar) THEN
242 : CALL section_vals_val_get(replica_section, "COLLECTIVE", &
243 18 : i_rep_section=i_rep, n_rep_val=n_rep)
244 18 : IF (n_rep /= 0) THEN
245 : ! Read the values of the collective variables
246 10 : NULLIFY (initial_colvars)
247 : CALL section_vals_val_get(replica_section, "COLLECTIVE", &
248 10 : i_rep_section=i_rep, r_vals=initial_colvars)
249 10 : check = (neb_env%nsize_int == SIZE(initial_colvars))
250 10 : CPASSERT(check)
251 30 : coords%int(:, i_rep) = initial_colvars
252 : ELSE
253 : ! Compute the values of the collective variables
254 8 : CALL eval_colvar(neb_env%force_env, coords%xyz(:, i_rep), coords%int(:, i_rep))
255 : END IF
256 : END IF
257 : ! Dump cartesian and colvar info..
258 160 : CALL dump_replica_coordinates(particle_set, coords, i_rep, i_rep, iw, neb_env%use_colvar)
259 : ! Setup Velocities
260 354 : IF (skip_vel_section) THEN
261 : CALL neb_initialize_velocity(vels%wrk, neb_section, particle_set, &
262 132 : i_rep, iw, globenv, neb_env)
263 : ELSE
264 : vel_section => section_vals_get_subs_vals(replica_section, "VELOCITY", &
265 28 : i_rep_section=i_rep)
266 : CALL section_vals_val_get(vel_section, "_DEFAULT_KEYWORD_", &
267 28 : n_rep_val=nval)
268 : ! Setup Velocities for collective or cartesian coordinates
269 28 : IF (neb_env%use_colvar) THEN
270 10 : nvar = SIZE(vels%wrk, 1)
271 10 : CPASSERT(nval == nvar)
272 20 : DO ivar = 1, nvar
273 : CALL section_vals_val_get(vel_section, "_DEFAULT_KEYWORD_", &
274 10 : i_rep_val=ivar, r_vals=rptr)
275 20 : vels%wrk(ivar, i_rep) = rptr(1)
276 : END DO
277 : ELSE
278 18 : natom = SIZE(particle_set)
279 18 : CPASSERT(nval == natom)
280 948 : DO iatom = 1, natom
281 : CALL section_vals_val_get(vel_section, "_DEFAULT_KEYWORD_", &
282 930 : i_rep_val=iatom, r_vals=rptr)
283 930 : ic = 3*(iatom - 1)
284 6510 : vels%wrk(ic + 1:ic + 3, i_rep) = rptr(1:3)
285 : ! Initially set shell velocities to core velocity
286 930 : shell_index = particle_set(iatom)%shell_index
287 948 : IF (shell_index /= 0) THEN
288 760 : is = 3*(natom + shell_index - 1)
289 5320 : vels%wrk(is + 1:is + 3, i_rep) = vels%wrk(ic + 1:ic + 3, i_rep)
290 : END IF
291 : END DO
292 : END IF
293 : END IF
294 : END DO ! i_rep
295 102 : ALLOCATE (distance(neb_nr_replica - 1))
296 34 : IF (input_nr_replica < neb_nr_replica) THEN
297 : ! Interpolate missing replicas
298 12 : nr_replica_to_interpolate = neb_nr_replica - input_nr_replica
299 104 : distance = 0.0_dp
300 12 : IF (iw > 0) THEN
301 2 : WRITE (iw, '(T2,A,I0,A)') 'NEB| Interpolating ', nr_replica_to_interpolate, ' missing Replica.'
302 : END IF
303 64 : DO WHILE (nr_replica_to_interpolate > 0)
304 : ! Compute distances between known images to find the interval
305 : ! where to add a new image
306 358 : DO j = 1, input_nr_replica - 1
307 : CALL neb_replica_distance(particle_set, coords, j, j + 1, distance(j), iw, &
308 358 : rotate=neb_env%align_frames)
309 : END DO
310 410 : jtarg = MAXLOC(distance(1:input_nr_replica), 1)
311 52 : IF (iw > 0) THEN
312 3 : WRITE (iw, '(T2,3(A,I0),A)') 'NEB| Interpolating Nr. ', &
313 3 : nr_replica_to_interpolate, ' missing Replica between Replica Nr. ', &
314 6 : jtarg, ' and ', jtarg + 1, '.'
315 : END IF
316 52 : input_nr_replica = input_nr_replica + 1
317 52 : nr_replica_to_interpolate = nr_replica_to_interpolate - 1
318 : ! Interpolation is a simple bisection in XYZ
319 12630 : coords%xyz(:, jtarg + 2:input_nr_replica) = coords%xyz(:, jtarg + 1:input_nr_replica - 1)
320 4780 : coords%xyz(:, jtarg + 1) = (coords%xyz(:, jtarg) + coords%xyz(:, jtarg + 2))/2.0_dp
321 52 : IF (neb_env%use_colvar) THEN
322 : ! Interpolation is a simple bisection also in internal coordinates
323 : ! in this case the XYZ coordinates need only as a starting point for computing
324 : ! the potential energy function. The reference are the internal coordinates
325 : ! interpolated here after..
326 6 : coords%int(:, jtarg + 2:input_nr_replica) = coords%int(:, jtarg + 1:input_nr_replica - 1)
327 4 : coords%int(:, jtarg + 1) = (coords%int(:, jtarg) + coords%int(:, jtarg + 2))/2.0_dp
328 : END IF
329 12530 : vels%wrk(:, jtarg + 2:input_nr_replica) = vels%wrk(:, jtarg + 1:input_nr_replica - 1)
330 4680 : vels%wrk(:, jtarg + 1) = 0.0_dp
331 : CALL dump_replica_coordinates(particle_set, coords, jtarg + 1, &
332 52 : input_nr_replica, iw, neb_env%use_colvar)
333 : CALL neb_initialize_velocity(vels%wrk, neb_section, particle_set, &
334 64 : jtarg + 1, iw, globenv, neb_env)
335 : END DO
336 : END IF
337 8126 : vels%wrk(:, 1) = 0.0_dp
338 8126 : vels%wrk(:, neb_nr_replica) = 0.0_dp
339 : ! If we perform a DIIS optimization we don't need velocities
340 37092 : IF (neb_env%opt_type == band_diis_opt) vels%wrk = 0.0_dp
341 : ! Compute distances between replicas and in case of Cartesian Coordinates
342 : ! Rotate the frames in order to minimize the RMSD
343 212 : DO j = 1, input_nr_replica - 1
344 : CALL neb_replica_distance(particle_set, coords, j, j + 1, distance(j), iw, &
345 212 : rotate=neb_env%align_frames)
346 : END DO
347 34 : DEALLOCATE (distance)
348 :
349 34 : CALL timestop(handle)
350 :
351 68 : END SUBROUTINE build_replica_coords
352 :
353 : ! **************************************************************************************************
354 : !> \brief Driver to compute energy and forces within a NEB,
355 : !> Based on the use of the replica_env
356 : !> \param rep_env ...
357 : !> \param neb_env ...
358 : !> \param coords ...
359 : !> \param energies ...
360 : !> \param forces ...
361 : !> \param particle_set ...
362 : !> \param output_unit ...
363 : !> \author Teodoro Laino 09.2006
364 : ! **************************************************************************************************
365 908 : SUBROUTINE neb_calc_energy_forces(rep_env, neb_env, coords, energies, forces, &
366 : particle_set, output_unit)
367 : TYPE(replica_env_type), POINTER :: rep_env
368 : TYPE(neb_type), OPTIONAL, POINTER :: neb_env
369 : TYPE(neb_var_type), POINTER :: coords
370 : REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: energies
371 : TYPE(neb_var_type), POINTER :: forces
372 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
373 : INTEGER, INTENT(IN) :: output_unit
374 :
375 : CHARACTER(len=*), PARAMETER :: routineN = 'neb_calc_energy_forces'
376 : CHARACTER(LEN=1), DIMENSION(3), PARAMETER :: lab = (/"X", "Y", "Z"/)
377 :
378 : INTEGER :: handle, i, irep, j, n_int, n_rep, &
379 : n_rep_neb, nsize_wrk
380 908 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: tangent, tmp_a, tmp_b
381 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: cvalues, Mmatrix, Mmatrix_tmp
382 :
383 908 : CALL timeset(routineN, handle)
384 908 : n_int = neb_env%nsize_int
385 908 : n_rep_neb = neb_env%number_of_replica
386 908 : n_rep = rep_env%nrep
387 908 : nsize_wrk = coords%size_wrk(1)
388 5948 : energies = 0.0_dp
389 2748 : ALLOCATE (cvalues(n_int, n_rep))
390 2748 : ALLOCATE (Mmatrix_tmp(n_int*n_int, n_rep))
391 2748 : ALLOCATE (Mmatrix(n_int*n_int, n_rep_neb))
392 908 : IF (output_unit > 0) WRITE (output_unit, '(/,T2,A)') "NEB| Computing Energies and Forces"
393 3700 : DO irep = 1, n_rep_neb, n_rep
394 8346 : DO j = 0, n_rep - 1
395 8346 : IF (irep + j <= n_rep_neb) THEN
396 : ! If the number of replica in replica_env and the number of replica
397 : ! used in the NEB does not match, the other replica in replica_env
398 : ! just compute energies and forces keeping the fixed coordinates and
399 : ! forces
400 2178252 : rep_env%r(:, j + 1) = coords%xyz(:, irep + j)
401 : END IF
402 : END DO
403 : ! Fix file name for BAND replicas.. Each BAND replica has its own file
404 : ! independently from the number of replicas in replica_env..
405 2792 : CALL handle_band_file_names(rep_env, irep, n_rep_neb, neb_env%istep)
406 : ! Let's select the potential we want to use for the band calculation
407 5512 : SELECT CASE (neb_env%pot_type)
408 : CASE (pot_neb_full)
409 : ! Full potential Energy
410 2720 : CALL rep_env_calc_e_f(rep_env, calc_f=.TRUE.)
411 : CASE (pot_neb_fe)
412 : ! Free Energy Case
413 0 : CALL perform_replica_md(rep_env, coords, irep, n_rep_neb, cvalues, Mmatrix_tmp)
414 : CASE (pot_neb_me)
415 : ! Minimum Potential Energy Case
416 2792 : CALL perform_replica_geo(rep_env, coords, irep, n_rep_neb, cvalues, Mmatrix_tmp)
417 : END SELECT
418 :
419 9254 : DO j = 0, n_rep - 1
420 8346 : IF (irep + j <= n_rep_neb) THEN
421 : ! Copy back Forces and Energies
422 2166252 : forces%wrk(:, irep + j) = rep_env%f(1:nsize_wrk, j + 1)
423 5040 : energies(irep + j) = rep_env%f(rep_env%ndim + 1, j + 1)
424 9960 : SELECT CASE (neb_env%pot_type)
425 : CASE (pot_neb_full)
426 : ! Dump Info
427 4920 : IF (output_unit > 0) THEN
428 : WRITE (output_unit, '(T2,A,I5,A,I5,A)') &
429 0 : "NEB| REPLICA Nr.", irep + j, "- Energy and Forces"
430 : WRITE (output_unit, '(T2,A,T43,A,T57,F24.12)') &
431 0 : "NEB|", "Total energy:", rep_env%f(rep_env%ndim + 1, j + 1)
432 0 : WRITE (output_unit, '(T2,"NEB|",T10,"ATOM",T33,3(9X,A,7X))') lab(1), lab(2), lab(3)
433 0 : DO i = 1, SIZE(particle_set)
434 : WRITE (output_unit, '(T2,"NEB|",T12,A,T30,3(2X,F15.9))') &
435 0 : particle_set(i)%atomic_kind%name, &
436 0 : rep_env%f((i - 1)*3 + 1:(i - 1)*3 + 3, j + 1)
437 : END DO
438 : END IF
439 : CASE (pot_neb_fe, pot_neb_me)
440 : ! Let's update the cartesian coordinates. This will make
441 : ! easier the next evaluation of energies and forces...
442 12480 : coords%xyz(:, irep + j) = rep_env%r(1:rep_env%ndim, j + 1)
443 240 : Mmatrix(:, irep + j) = Mmatrix_tmp(:, j + 1)
444 5160 : IF (output_unit > 0) THEN
445 : WRITE (output_unit, '(/,T2,A,I5,A,I5,A)') &
446 60 : "NEB| REPLICA Nr.", irep + j, "- Energy, Collective Variables, Forces"
447 : WRITE (output_unit, '(T2,A,T43,A,T57,F24.12)') &
448 60 : "NEB|", "Total energy:", rep_env%f(rep_env%ndim + 1, j + 1)
449 : WRITE (output_unit, &
450 60 : '(T2,"NEB|",T10,"CV Nr.",12X,"Expected COLVAR",5X,"Present COLVAR",10X,"Forces")')
451 120 : DO i = 1, n_int
452 : WRITE (output_unit, '(T2,"NEB|",T12,I2,7X,3(5X,F15.9))') &
453 120 : i, coords%int(i, irep + j), cvalues(i, j + 1), rep_env%f(i, j + 1)
454 : END DO
455 : END IF
456 : END SELECT
457 : END IF
458 : END DO
459 : END DO
460 908 : DEALLOCATE (cvalues)
461 908 : DEALLOCATE (Mmatrix_tmp)
462 908 : IF (PRESENT(neb_env)) THEN
463 : ! First identify the image of the chain with the higher potential energy
464 : ! First and last point of the band are never considered
465 4132 : neb_env%nr_HE_image = MAXLOC(energies(2:n_rep_neb - 1), 1) + 1
466 2724 : ALLOCATE (tangent(nsize_wrk))
467 : ! Then modify image forces accordingly to the scheme chosen for the
468 : ! calculation.
469 908 : neb_env%spring_energy = 0.0_dp
470 908 : IF (neb_env%optimize_end_points) THEN
471 1290 : ALLOCATE (tmp_a(SIZE(forces%wrk, 1)))
472 860 : ALLOCATE (tmp_b(SIZE(forces%wrk, 1)))
473 212314 : tmp_a(:) = forces%wrk(:, 1)
474 212314 : tmp_b(:) = forces%wrk(:, SIZE(forces%wrk, 2))
475 : END IF
476 5040 : DO i = 2, neb_env%number_of_replica
477 4132 : CALL get_tangent(neb_env, coords, i, tangent, energies, output_unit)
478 : CALL get_neb_force(neb_env, tangent, coords, i, forces, Mmatrix=Mmatrix, &
479 5040 : iw=output_unit)
480 : END DO
481 908 : IF (neb_env%optimize_end_points) THEN
482 212314 : forces%wrk(:, 1) = tmp_a ! Image A
483 212314 : forces%wrk(:, SIZE(forces%wrk, 2)) = tmp_b ! Image B
484 430 : DEALLOCATE (tmp_a)
485 430 : DEALLOCATE (tmp_b)
486 : ELSE
487 : ! Nullify forces on the two end points images
488 37102 : forces%wrk(:, 1) = 0.0_dp ! Image A
489 37102 : forces%wrk(:, SIZE(forces%wrk, 2)) = 0.0_dp ! Image B
490 : END IF
491 908 : DEALLOCATE (tangent)
492 : END IF
493 908 : DEALLOCATE (Mmatrix)
494 908 : CALL timestop(handle)
495 1816 : END SUBROUTINE neb_calc_energy_forces
496 :
497 : ! **************************************************************************************************
498 : !> \brief Driver to perform an MD run on each single replica to
499 : !> compute specifically Free Energies in a NEB scheme
500 : !> \param rep_env ...
501 : !> \param coords ...
502 : !> \param irep ...
503 : !> \param n_rep_neb ...
504 : !> \param cvalues ...
505 : !> \param Mmatrix ...
506 : !> \author Teodoro Laino 01.2007
507 : ! **************************************************************************************************
508 0 : SUBROUTINE perform_replica_md(rep_env, coords, irep, n_rep_neb, cvalues, Mmatrix)
509 : TYPE(replica_env_type), POINTER :: rep_env
510 : TYPE(neb_var_type), POINTER :: coords
511 : INTEGER, INTENT(IN) :: irep, n_rep_neb
512 : REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: cvalues, Mmatrix
513 :
514 : CHARACTER(len=*), PARAMETER :: routineN = 'perform_replica_md'
515 :
516 : INTEGER :: handle, handle2, ierr, j, n_el
517 : LOGICAL :: explicit
518 : TYPE(cp_logger_type), POINTER :: logger
519 : TYPE(f_env_type), POINTER :: f_env
520 : TYPE(global_environment_type), POINTER :: globenv
521 : TYPE(section_vals_type), POINTER :: md_section, root_section
522 :
523 0 : CALL timeset(routineN, handle)
524 : CALL f_env_add_defaults(f_env_id=rep_env%f_env_id, f_env=f_env, &
525 0 : handle=handle2)
526 0 : logger => cp_get_default_logger()
527 : CALL force_env_get(f_env%force_env, globenv=globenv, &
528 0 : root_section=root_section)
529 0 : j = rep_env%local_rep_indices(1) - 1
530 0 : n_el = 3*rep_env%nparticle
531 0 : Mmatrix = 0.0_dp
532 : ! Syncronize position on the replica procs
533 0 : CALL set_pos(rep_env%f_env_id, rep_env%r(:, j + 1), n_el, ierr)
534 0 : CPASSERT(ierr == 0)
535 : !
536 0 : IF (irep + j <= n_rep_neb) THEN
537 0 : logger%iter_info%iteration(2) = irep + j
538 0 : CALL remove_restart_info(root_section)
539 0 : md_section => section_vals_get_subs_vals(root_section, "MOTION%MD")
540 0 : CALL section_vals_get(md_section, explicit=explicit)
541 0 : CPASSERT(explicit)
542 : ! Let's syncronize the target of Collective Variables for this run
543 0 : CALL set_colvars_target(coords%int(:, irep + j), f_env%force_env)
544 : ! Do a molecular dynamics and get back the derivative
545 : ! of the free energy w.r.t. the colvar and the metric tensor
546 0 : CALL qs_mol_dyn(f_env%force_env, globenv=globenv)
547 : ! Collect the equilibrated coordinates
548 0 : CALL get_pos(rep_env%f_env_id, rep_env%r(1:n_el, j + 1), n_el, ierr)
549 0 : CPASSERT(ierr == 0)
550 : ! Write he gradients in the colvar coordinates into the replica_env array
551 : ! and copy back also the metric tensor..
552 : ! work in progress..
553 0 : CPABORT("")
554 0 : rep_env%f(:, j + 1) = 0.0_dp
555 0 : Mmatrix = 0.0_dp
556 : ELSE
557 0 : rep_env%r(:, j + 1) = 0.0_dp
558 0 : rep_env%f(:, j + 1) = 0.0_dp
559 0 : cvalues(:, j + 1) = 0.0_dp
560 0 : Mmatrix(:, j + 1) = 0.0_dp
561 : END IF
562 0 : CALL rep_env_sync(rep_env, rep_env%f)
563 0 : CALL rep_env_sync(rep_env, rep_env%r)
564 0 : CALL rep_env_sync(rep_env, cvalues)
565 0 : CALL rep_env_sync(rep_env, Mmatrix)
566 0 : CALL f_env_rm_defaults(f_env=f_env, ierr=ierr, handle=handle2)
567 0 : CPASSERT(ierr == 0)
568 0 : CALL timestop(handle)
569 0 : END SUBROUTINE perform_replica_md
570 :
571 : ! **************************************************************************************************
572 : !> \brief Driver to perform a GEO_OPT run on each single replica to
573 : !> compute specifically minimum energies in a collective variable
574 : !> NEB scheme
575 : !> \param rep_env ...
576 : !> \param coords ...
577 : !> \param irep ...
578 : !> \param n_rep_neb ...
579 : !> \param cvalues ...
580 : !> \param Mmatrix ...
581 : !> \author Teodoro Laino 05.2007
582 : ! **************************************************************************************************
583 72 : SUBROUTINE perform_replica_geo(rep_env, coords, irep, n_rep_neb, cvalues, Mmatrix)
584 : TYPE(replica_env_type), POINTER :: rep_env
585 : TYPE(neb_var_type), POINTER :: coords
586 : INTEGER, INTENT(IN) :: irep, n_rep_neb
587 : REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: cvalues, Mmatrix
588 :
589 : CHARACTER(len=*), PARAMETER :: routineN = 'perform_replica_geo'
590 :
591 : INTEGER :: handle, handle2, ierr, j, n_el
592 : LOGICAL :: explicit
593 : TYPE(cp_logger_type), POINTER :: logger
594 : TYPE(f_env_type), POINTER :: f_env
595 : TYPE(global_environment_type), POINTER :: globenv
596 : TYPE(section_vals_type), POINTER :: geoopt_section, root_section
597 :
598 72 : CALL timeset(routineN, handle)
599 : CALL f_env_add_defaults(f_env_id=rep_env%f_env_id, f_env=f_env, &
600 72 : handle=handle2)
601 72 : logger => cp_get_default_logger()
602 : CALL force_env_get(f_env%force_env, globenv=globenv, &
603 72 : root_section=root_section)
604 72 : j = rep_env%local_rep_indices(1) - 1
605 72 : n_el = 3*rep_env%nparticle
606 360 : Mmatrix = 0.0_dp
607 : ! Syncronize position on the replica procs
608 72 : CALL set_pos(rep_env%f_env_id, rep_env%r(:, j + 1), n_el, ierr)
609 72 : CPASSERT(ierr == 0)
610 72 : IF (irep + j <= n_rep_neb) THEN
611 60 : logger%iter_info%iteration(2) = irep + j
612 60 : CALL remove_restart_info(root_section)
613 60 : geoopt_section => section_vals_get_subs_vals(root_section, "MOTION%GEO_OPT")
614 60 : CALL section_vals_get(geoopt_section, explicit=explicit)
615 60 : CPASSERT(explicit)
616 : ! Let's syncronize the target of Collective Variables for this run
617 60 : CALL set_colvars_target(coords%int(:, irep + j), f_env%force_env)
618 : ! Do a geometry optimization..
619 60 : CALL cp_geo_opt(f_env%force_env, globenv=globenv)
620 : ! Once the geometry optimization is ended let's do a single run
621 : ! without any constraints/restraints
622 : CALL force_env_calc_energy_force(f_env%force_env, &
623 60 : calc_force=.TRUE., skip_external_control=.TRUE.)
624 : ! Collect the optimized coordinates
625 60 : CALL get_pos(rep_env%f_env_id, rep_env%r(1:n_el, j + 1), n_el, ierr)
626 60 : CPASSERT(ierr == 0)
627 : ! Collect the gradients in cartesian coordinates
628 60 : CALL get_force(rep_env%f_env_id, rep_env%f(1:n_el, j + 1), n_el, ierr)
629 60 : CPASSERT(ierr == 0)
630 : ! Copy the energy
631 60 : CALL get_energy(rep_env%f_env_id, rep_env%f(n_el + 1, j + 1), ierr)
632 60 : CPASSERT(ierr == 0)
633 : ! The gradients in the colvar coordinates
634 : CALL get_clv_force(f_env%force_env, rep_env%f(1:n_el, j + 1), rep_env%r(1:n_el, j + 1), &
635 60 : SIZE(coords%xyz, 1), SIZE(coords%wrk, 1), cvalues(:, j + 1), Mmatrix(:, j + 1))
636 : ELSE
637 624 : rep_env%r(:, j + 1) = 0.0_dp
638 636 : rep_env%f(:, j + 1) = 0.0_dp
639 24 : cvalues(:, j + 1) = 0.0_dp
640 24 : Mmatrix(:, j + 1) = 0.0_dp
641 : END IF
642 72 : CALL rep_env_sync(rep_env, rep_env%f)
643 72 : CALL rep_env_sync(rep_env, rep_env%r)
644 72 : CALL rep_env_sync(rep_env, cvalues)
645 72 : CALL rep_env_sync(rep_env, Mmatrix)
646 72 : CALL f_env_rm_defaults(f_env=f_env, ierr=ierr, handle=handle2)
647 72 : CPASSERT(ierr == 0)
648 72 : CALL timestop(handle)
649 72 : END SUBROUTINE perform_replica_geo
650 :
651 : ! **************************************************************************************************
652 : !> \brief Computes the tangent for point i of the NEB chain
653 : !> \param neb_env ...
654 : !> \param coords ...
655 : !> \param i ...
656 : !> \param tangent ...
657 : !> \param energies ...
658 : !> \param iw ...
659 : !> \author Teodoro Laino 09.2006
660 : ! **************************************************************************************************
661 4132 : SUBROUTINE get_tangent(neb_env, coords, i, tangent, energies, iw)
662 : TYPE(neb_type), POINTER :: neb_env
663 : TYPE(neb_var_type), POINTER :: coords
664 : INTEGER, INTENT(IN) :: i
665 : REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: tangent
666 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: energies
667 : INTEGER, INTENT(IN) :: iw
668 :
669 : REAL(KINd=dp) :: distance0, distance1, distance2, DVmax, &
670 : Dvmin
671 :
672 4132 : CPASSERT(ASSOCIATED(coords))
673 833710 : tangent(:) = 0.0_dp
674 : ! For the last point we don't need any tangent..
675 4132 : IF (i == neb_env%number_of_replica) RETURN
676 : ! Several kind of tangents implemented...
677 3224 : SELECT CASE (neb_env%id_type)
678 : CASE (do_eb)
679 38792 : tangent(:) = 0.0_dp
680 : CASE (do_b_neb)
681 : CALL neb_replica_distance(coords=coords, i0=i, i=i - 1, distance=distance1, iw=iw, &
682 126 : rotate=.FALSE.)
683 : CALL neb_replica_distance(coords=coords, i0=i + 1, i=i, distance=distance2, iw=iw, &
684 126 : rotate=.FALSE.)
685 : tangent(:) = (coords%wrk(:, i) - coords%wrk(:, i - 1))/distance1 + &
686 6552 : (coords%wrk(:, i + 1) - coords%wrk(:, i))/distance2
687 : CASE (do_it_neb, do_ci_neb, do_d_neb)
688 1974 : IF ((energies(i + 1) .GT. energies(i)) .AND. (energies(i) .GT. (energies(i - 1)))) THEN
689 226914 : tangent(:) = coords%wrk(:, i + 1) - coords%wrk(:, i)
690 1282 : ELSE IF ((energies(i + 1) .LT. energies(i)) .AND. (energies(i) .LT. (energies(i - 1)))) THEN
691 35162 : tangent(:) = coords%wrk(:, i) - coords%wrk(:, i - 1)
692 : ELSE
693 1188 : DVmax = MAX(ABS(energies(i + 1) - energies(i)), ABS(energies(i - 1) - energies(i)))
694 1188 : DVmin = MIN(ABS(energies(i + 1) - energies(i)), ABS(energies(i - 1) - energies(i)))
695 1188 : IF (energies(i + 1) .GE. energies(i - 1)) THEN
696 26028 : tangent(:) = (coords%wrk(:, i + 1) - coords%wrk(:, i))*DVmax + (coords%wrk(:, i) - coords%wrk(:, i - 1))*DVmin
697 : ELSE
698 231190 : tangent(:) = (coords%wrk(:, i + 1) - coords%wrk(:, i))*DVmin + (coords%wrk(:, i) - coords%wrk(:, i - 1))*DVmax
699 : END IF
700 : END IF
701 : CASE (do_sm)
702 : ! String method..
703 22502 : tangent(:) = 0.0_dp
704 : END SELECT
705 584294 : distance0 = SQRT(DOT_PRODUCT(tangent(:), tangent(:)))
706 526970 : IF (distance0 /= 0.0_dp) tangent(:) = tangent(:)/distance0
707 : END SUBROUTINE get_tangent
708 :
709 : ! **************************************************************************************************
710 : !> \brief Computes the forces for point i of the NEB chain
711 : !> \param neb_env ...
712 : !> \param tangent ...
713 : !> \param coords ...
714 : !> \param i ...
715 : !> \param forces ...
716 : !> \param tag ...
717 : !> \param Mmatrix ...
718 : !> \param iw ...
719 : !> \author Teodoro Laino 09.2006
720 : ! **************************************************************************************************
721 4630 : RECURSIVE SUBROUTINE get_neb_force(neb_env, tangent, coords, i, forces, tag, Mmatrix, &
722 : iw)
723 : TYPE(neb_type), POINTER :: neb_env
724 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: tangent
725 : TYPE(neb_var_type), POINTER :: coords
726 : INTEGER, INTENT(IN) :: i
727 : TYPE(neb_var_type), POINTER :: forces
728 : INTEGER, INTENT(IN), OPTIONAL :: tag
729 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: Mmatrix
730 : INTEGER, INTENT(IN) :: iw
731 :
732 : INTEGER :: j, my_tag, nsize_wrk
733 : REAL(KIND=dp) :: distance1, distance2, fac, tmp
734 4630 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: dtmp1, wrk
735 :
736 4630 : my_tag = neb_env%id_type
737 4630 : IF (PRESENT(tag)) my_tag = tag
738 4630 : CPASSERT(ASSOCIATED(forces))
739 4630 : CPASSERT(ASSOCIATED(coords))
740 4630 : nsize_wrk = coords%size_wrk(1)
741 : ! All methods but not the classical elastic band will skip the force
742 : ! calculation for the last frame of the band
743 3336 : SELECT CASE (my_tag)
744 : CASE (do_b_neb, do_it_neb, do_ci_neb, do_d_neb)
745 3336 : IF (i == neb_env%number_of_replica) RETURN
746 : CASE (do_sm)
747 : ! String Method
748 : ! The forces do not require any projection. Reparametrization required
749 : ! after the update of the replica.
750 420 : CALL cite_reference(E2002)
751 4630 : RETURN
752 : END SELECT
753 : ! otherwise proceeed normally..
754 10416 : ALLOCATE (wrk(nsize_wrk))
755 : ! Spring Energy
756 : CALL neb_replica_distance(coords=coords, i0=i - 1, i=i, distance=distance1, iw=iw, &
757 3472 : rotate=.FALSE.)
758 3472 : tmp = distance1 - neb_env%avg_distance
759 3472 : neb_env%spring_energy = neb_env%spring_energy + 0.5_dp*neb_env%k*tmp**2
760 874 : SELECT CASE (my_tag)
761 : CASE (do_eb)
762 874 : CALL cite_reference(Elber1987)
763 : ! Elastic band - Hamiltonian formulation according the original Karplus/Elber
764 : ! formulation
765 1748 : ALLOCATE (dtmp1(nsize_wrk))
766 : ! derivatives of the spring
767 874 : tmp = distance1 - neb_env%avg_distance
768 45448 : dtmp1(:) = 1.0_dp/distance1*(coords%wrk(:, i) - coords%wrk(:, i - 1))
769 45448 : wrk(:) = neb_env%k*tmp*dtmp1
770 45448 : forces%wrk(:, i) = forces%wrk(:, i) - wrk
771 45448 : forces%wrk(:, i - 1) = forces%wrk(:, i - 1) + wrk
772 : ! derivatives due to the average length of the spring
773 874 : fac = 1.0_dp/(neb_env%avg_distance*REAL(neb_env%number_of_replica - 1, KIND=dp))
774 45448 : wrk(:) = neb_env%k*fac*(coords%wrk(:, i) - coords%wrk(:, i - 1))
775 874 : tmp = 0.0_dp
776 7880 : DO j = 2, neb_env%number_of_replica
777 : CALL neb_replica_distance(coords=coords, i0=j - 1, i=j, distance=distance1, iw=iw, &
778 7006 : rotate=.FALSE.)
779 7880 : tmp = tmp + distance1 - neb_env%avg_distance
780 : END DO
781 45448 : forces%wrk(:, i) = forces%wrk(:, i) + wrk*tmp
782 45448 : forces%wrk(:, i - 1) = forces%wrk(:, i - 1) - wrk*tmp
783 874 : DEALLOCATE (dtmp1)
784 : CASE (do_b_neb)
785 : ! Bisection NEB
786 126 : CALL cite_reference(Jonsson1998)
787 6552 : wrk(:) = (coords%wrk(:, i + 1) - 2.0_dp*coords%wrk(:, i) + coords%wrk(:, i - 1))
788 6552 : tmp = neb_env%k*DOT_PRODUCT(wrk, tangent)
789 6552 : wrk(:) = forces%wrk(:, i) - dot_product_band(neb_env, forces%wrk(:, i), tangent, Mmatrix)*tangent
790 6552 : forces%wrk(:, i) = wrk + tmp*tangent
791 : CASE (do_it_neb)
792 : ! Improved tangent NEB
793 1236 : CALL cite_reference(Jonsson2000_1)
794 : CALL neb_replica_distance(coords=coords, i0=i, i=i + 1, distance=distance1, iw=iw, &
795 1236 : rotate=.FALSE.)
796 : CALL neb_replica_distance(coords=coords, i0=i - 1, i=i, distance=distance2, iw=iw, &
797 1236 : rotate=.FALSE.)
798 1236 : tmp = neb_env%k*(distance1 - distance2)
799 309648 : wrk(:) = forces%wrk(:, i) - dot_product_band(neb_env, forces%wrk(:, i), tangent, Mmatrix)*tangent
800 309648 : forces%wrk(:, i) = wrk + tmp*tangent
801 : CASE (do_ci_neb)
802 : ! Climbing Image NEB
803 858 : CALL cite_reference(Jonsson2000_2)
804 858 : IF (neb_env%istep <= neb_env%nsteps_it .OR. i /= neb_env%nr_HE_image) THEN
805 498 : CALL get_neb_force(neb_env, tangent, coords, i, forces, do_it_neb, Mmatrix, iw)
806 : ELSE
807 189990 : wrk(:) = forces%wrk(:, i)
808 360 : tmp = -2.0_dp*dot_product_band(neb_env, wrk, tangent, Mmatrix)
809 189990 : forces%wrk(:, i) = wrk + tmp*tangent
810 : END IF
811 : CASE (do_d_neb)
812 : ! Doubly NEB
813 378 : CALL cite_reference(Wales2004)
814 756 : ALLOCATE (dtmp1(nsize_wrk))
815 19656 : dtmp1(:) = forces%wrk(:, i) - dot_product_band(neb_env, forces%wrk(:, i), tangent, Mmatrix)*tangent
816 19656 : forces%wrk(:, i) = dtmp1
817 19656 : tmp = SQRT(DOT_PRODUCT(dtmp1, dtmp1))
818 19656 : dtmp1(:) = dtmp1(:)/tmp
819 : ! Project out only the spring component interfering with the
820 : ! orthogonal gradient of the band
821 19656 : wrk(:) = (coords%wrk(:, i + 1) - 2.0_dp*coords%wrk(:, i) + coords%wrk(:, i - 1))
822 19656 : tmp = DOT_PRODUCT(wrk, dtmp1)
823 19656 : dtmp1(:) = neb_env%k*(wrk(:) - tmp*dtmp1(:))
824 19656 : forces%wrk(:, i) = forces%wrk(:, i) + dtmp1(:)
825 3850 : DEALLOCATE (dtmp1)
826 : END SELECT
827 3472 : DEALLOCATE (wrk)
828 : END SUBROUTINE get_neb_force
829 :
830 : ! **************************************************************************************************
831 : !> \brief Handles the dot_product when using colvar.. in this case
832 : !> the scalar product needs to take into account the metric
833 : !> tensor
834 : !> \param neb_env ...
835 : !> \param array1 ...
836 : !> \param array2 ...
837 : !> \param array3 ...
838 : !> \return ...
839 : !> \author Teodoro Laino 09.2006
840 : ! **************************************************************************************************
841 2100 : FUNCTION dot_product_band(neb_env, array1, array2, array3) RESULT(value)
842 : TYPE(neb_type), POINTER :: neb_env
843 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: array1, array2
844 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: array3
845 : REAL(KIND=dp) :: value
846 :
847 : INTEGER :: nsize_int
848 : LOGICAL :: check
849 :
850 2100 : IF (neb_env%use_colvar) THEN
851 72 : nsize_int = neb_env%nsize_int
852 : check = ((SIZE(array1) /= SIZE(array2)) .OR. &
853 : (SIZE(array1) /= nsize_int) .OR. &
854 216 : (SIZE(array3) /= nsize_int*nsize_int))
855 : ! This condition should always be satisfied..
856 0 : CPASSERT(check)
857 792 : value = DOT_PRODUCT(MATMUL(RESHAPE(array3, (/nsize_int, nsize_int/)), array1), array2)
858 : ELSE
859 525702 : value = DOT_PRODUCT(array1, array2)
860 : END IF
861 2100 : END FUNCTION dot_product_band
862 :
863 : ! **************************************************************************************************
864 : !> \brief Reorient iteratively all images of the NEB chain in order to
865 : !> have always the smaller RMSD between two following images
866 : !> \param rotate_frames ...
867 : !> \param particle_set ...
868 : !> \param coords ...
869 : !> \param vels ...
870 : !> \param iw ...
871 : !> \param distances ...
872 : !> \param number_of_replica ...
873 : !> \author Teodoro Laino 09.2006
874 : ! **************************************************************************************************
875 908 : SUBROUTINE reorient_images(rotate_frames, particle_set, coords, vels, iw, &
876 908 : distances, number_of_replica)
877 : LOGICAL, INTENT(IN) :: rotate_frames
878 : TYPE(particle_type), DIMENSION(:), OPTIONAL, &
879 : POINTER :: particle_set
880 : TYPE(neb_var_type), POINTER :: coords, vels
881 : INTEGER, INTENT(IN) :: iw
882 : REAL(KIND=dp), DIMENSION(:), OPTIONAL :: distances
883 : INTEGER, INTENT(IN) :: number_of_replica
884 :
885 : INTEGER :: i, k, kind
886 : LOGICAL :: check
887 : REAL(KIND=dp) :: xtmp
888 : REAL(KIND=dp), DIMENSION(3) :: tmp
889 : REAL(KIND=dp), DIMENSION(3, 3) :: rot
890 :
891 908 : rot = 0.0_dp
892 908 : rot(1, 1) = 1.0_dp
893 908 : rot(2, 2) = 1.0_dp
894 908 : rot(3, 3) = 1.0_dp
895 5040 : DO i = 2, number_of_replica
896 : ! The rotation of the replica is enabled exclusively when working in
897 : ! cartesian coordinates
898 4132 : IF (rotate_frames .AND. (coords%in_use == do_band_cartesian)) THEN
899 : CALL rmsd3(particle_set, coords%xyz(:, i), coords%xyz(:, i - 1), iw, &
900 540 : rotate=.TRUE., rot=rot)
901 : ! Rotate velocities
902 11796 : DO k = 1, SIZE(vels%xyz, 1)/3
903 11256 : kind = (k - 1)*3
904 45024 : tmp = vels%xyz(kind + 1:kind + 3, i)
905 45564 : vels%xyz(kind + 1:kind + 3, i) = MATMUL(TRANSPOSE(rot), tmp)
906 : END DO
907 : END IF
908 5040 : IF (PRESENT(distances)) THEN
909 4132 : check = SIZE(distances) == (number_of_replica - 1)
910 4132 : CPASSERT(check)
911 : xtmp = DOT_PRODUCT(coords%wrk(:, i) - coords%wrk(:, i - 1), &
912 833710 : coords%wrk(:, i) - coords%wrk(:, i - 1))
913 4132 : distances(i - 1) = SQRT(xtmp)
914 : END IF
915 : END DO
916 908 : END SUBROUTINE reorient_images
917 :
918 : ! **************************************************************************************************
919 : !> \brief Reparametrization of the replica for String Method with splines
920 : !> \param reparametrize_frames ...
921 : !> \param spline_order ...
922 : !> \param smoothing ...
923 : !> \param coords ...
924 : !> \param sline ...
925 : !> \param distances ...
926 : !> \author Teodoro Laino - Rodolphe Vuilleumier 09.2008
927 : ! **************************************************************************************************
928 402 : SUBROUTINE reparametrize_images(reparametrize_frames, spline_order, smoothing, &
929 402 : coords, sline, distances)
930 :
931 : LOGICAL, INTENT(IN) :: reparametrize_frames
932 : INTEGER, INTENT(IN) :: spline_order
933 : REAL(KIND=dp), INTENT(IN) :: smoothing
934 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: coords, sline
935 : REAL(KIND=dp), DIMENSION(:) :: distances
936 :
937 : INTEGER :: i, j
938 : REAL(KIND=dp) :: avg_distance, xtmp
939 402 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: tmp_coords
940 :
941 402 : IF (reparametrize_frames) THEN
942 168 : ALLOCATE (tmp_coords(SIZE(coords, 1), SIZE(coords, 2)))
943 24066 : tmp_coords(:, :) = coords
944 : ! Smoothing
945 420 : DO i = 2, SIZE(coords, 2) - 1
946 : coords(:, i) = tmp_coords(:, i)*(1.0_dp - 2.0_dp*smoothing) + &
947 19698 : tmp_coords(:, i - 1)*smoothing + tmp_coords(:, i + 1)*smoothing
948 : END DO
949 48090 : sline = coords - tmp_coords + sline
950 24066 : tmp_coords(:, :) = coords
951 : ! Reparametrization
952 84 : SELECT CASE (spline_order)
953 : CASE (1)
954 : ! Compute distances
955 462 : DO i = 2, SIZE(coords, 2)
956 21840 : xtmp = DOT_PRODUCT(coords(:, i) - coords(:, i - 1), coords(:, i) - coords(:, i - 1))
957 462 : distances(i - 1) = SQRT(xtmp)
958 : END DO
959 462 : avg_distance = SUM(distances)/REAL(SIZE(coords, 2) - 1, KIND=dp)
960 : ! Redistribute frames
961 420 : DO i = 2, SIZE(coords, 2) - 1
962 378 : xtmp = 0.0_dp
963 2022 : DO j = 1, SIZE(coords, 2) - 1
964 1980 : xtmp = xtmp + distances(j)
965 1980 : IF (xtmp > avg_distance*REAL(i - 1, KIND=dp)) THEN
966 378 : xtmp = (xtmp - avg_distance*REAL(i - 1, KIND=dp))/distances(j)
967 19656 : coords(:, i) = (1.0_dp - xtmp)*tmp_coords(:, j + 1) + xtmp*tmp_coords(:, j)
968 : EXIT
969 : END IF
970 : END DO
971 : END DO
972 : ! Re-compute distances
973 462 : DO i = 2, SIZE(coords, 2)
974 21840 : xtmp = DOT_PRODUCT(coords(:, i) - coords(:, i - 1), coords(:, i) - coords(:, i - 1))
975 462 : distances(i - 1) = SQRT(xtmp)
976 : END DO
977 : CASE DEFAULT
978 42 : CPWARN("String Method: Spline order greater than 1 not implemented.")
979 : END SELECT
980 48090 : sline = coords - tmp_coords + sline
981 42 : DEALLOCATE (tmp_coords)
982 : END IF
983 402 : END SUBROUTINE reparametrize_images
984 :
985 : ! **************************************************************************************************
986 : !> \brief Checks for convergence criteria during a NEB run
987 : !> \param neb_env ...
988 : !> \param Dcoords ...
989 : !> \param forces ...
990 : !> \return ...
991 : !> \author Teodoro Laino 10.2006
992 : ! **************************************************************************************************
993 2720 : FUNCTION check_convergence(neb_env, Dcoords, forces) RESULT(converged)
994 : TYPE(neb_type), POINTER :: neb_env
995 : TYPE(neb_var_type), POINTER :: Dcoords, forces
996 : LOGICAL :: converged
997 :
998 : CHARACTER(LEN=3), DIMENSION(4) :: labels
999 : INTEGER :: iw
1000 : REAL(KIND=dp) :: max_dr, max_force, my_max_dr, &
1001 : my_max_force, my_rms_dr, my_rms_force, &
1002 : rms_dr, rms_force
1003 : TYPE(cp_logger_type), POINTER :: logger
1004 : TYPE(section_vals_type), POINTER :: cc_section
1005 :
1006 544 : NULLIFY (logger, cc_section)
1007 544 : logger => cp_get_default_logger()
1008 544 : cc_section => section_vals_get_subs_vals(neb_env%neb_section, "CONVERGENCE_CONTROL")
1009 544 : CALL section_vals_val_get(cc_section, "MAX_DR", r_val=max_dr)
1010 544 : CALL section_vals_val_get(cc_section, "MAX_FORCE", r_val=max_force)
1011 544 : CALL section_vals_val_get(cc_section, "RMS_DR", r_val=rms_dr)
1012 544 : CALL section_vals_val_get(cc_section, "RMS_FORCE", r_val=rms_force)
1013 544 : converged = .FALSE.
1014 2720 : labels = " NO"
1015 562306 : my_max_dr = MAXVAL(ABS(Dcoords%wrk))
1016 562306 : my_max_force = MAXVAL(ABS(forces%wrk))
1017 562306 : my_rms_dr = SQRT(SUM(Dcoords%wrk*Dcoords%wrk)/REAL(SIZE(Dcoords%wrk, 1)*SIZE(Dcoords%wrk, 2), KIND=dp))
1018 562306 : my_rms_force = SQRT(SUM(forces%wrk*forces%wrk)/REAL(SIZE(forces%wrk, 1)*SIZE(forces%wrk, 2), KIND=dp))
1019 544 : IF (my_max_dr < max_dr) labels(1) = "YES"
1020 544 : IF (my_max_force < max_force) labels(2) = "YES"
1021 544 : IF (my_rms_dr < rms_dr) labels(3) = "YES"
1022 544 : IF (my_rms_force < rms_force) labels(4) = "YES"
1023 586 : IF (ALL(labels == "YES")) converged = .TRUE.
1024 :
1025 : iw = cp_print_key_unit_nr(logger, neb_env%neb_section, "CONVERGENCE_INFO", &
1026 544 : extension=".nebLog")
1027 544 : IF (iw > 0) THEN
1028 : ! Print convergence info
1029 543 : WRITE (iw, FMT='(A,A)') ' **************************************', &
1030 1086 : '*****************************************'
1031 : WRITE (iw, FMT='(1X,A,2X,F8.5,5X,"[",F8.5,"]",1X,T76,"(",A,")")') &
1032 543 : 'RMS DISPLACEMENT =', my_rms_dr, rms_dr, labels(3), &
1033 543 : 'MAX DISPLACEMENT =', my_max_dr, max_dr, labels(1), &
1034 543 : 'RMS FORCE =', my_rms_force, rms_force, labels(4), &
1035 1086 : 'MAX FORCE =', my_max_force, max_force, labels(2)
1036 543 : WRITE (iw, FMT='(A,A)') ' **************************************', &
1037 1086 : '*****************************************'
1038 : END IF
1039 : CALL cp_print_key_finished_output(iw, logger, neb_env%neb_section, &
1040 544 : "CONVERGENCE_INFO")
1041 544 : END FUNCTION check_convergence
1042 :
1043 72 : END MODULE neb_utils
|