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 Worker routines used by global optimization schemes
10 : !> \author Ole Schuett
11 : ! **************************************************************************************************
12 : MODULE glbopt_worker
13 : USE cp_subsys_types, ONLY: cp_subsys_get,&
14 : cp_subsys_type,&
15 : pack_subsys_particles,&
16 : unpack_subsys_particles
17 : USE f77_interface, ONLY: create_force_env,&
18 : destroy_force_env,&
19 : f_env_add_defaults,&
20 : f_env_rm_defaults,&
21 : f_env_type
22 : USE force_env_types, ONLY: force_env_get,&
23 : force_env_type
24 : USE geo_opt, ONLY: cp_geo_opt
25 : USE global_types, ONLY: global_environment_type
26 : USE input_section_types, ONLY: section_type,&
27 : section_vals_get_subs_vals,&
28 : section_vals_type,&
29 : section_vals_val_get,&
30 : section_vals_val_set
31 : USE kinds, ONLY: default_string_length,&
32 : dp
33 : USE md_run, ONLY: qs_mol_dyn
34 : USE mdctrl_types, ONLY: glbopt_mdctrl_data_type,&
35 : mdctrl_type
36 : USE message_passing, ONLY: mp_para_env_type
37 : USE physcon, ONLY: angstrom,&
38 : kelvin
39 : USE swarm_message, ONLY: swarm_message_add,&
40 : swarm_message_get,&
41 : swarm_message_type
42 : #include "../base/base_uses.f90"
43 :
44 : IMPLICIT NONE
45 : PRIVATE
46 :
47 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'glbopt_worker'
48 :
49 : PUBLIC :: glbopt_worker_init, glbopt_worker_finalize
50 : PUBLIC :: glbopt_worker_execute
51 : PUBLIC :: glbopt_worker_type
52 :
53 : TYPE glbopt_worker_type
54 : PRIVATE
55 : INTEGER :: id = -1
56 : INTEGER :: iw = -1
57 : INTEGER :: f_env_id = -1
58 : TYPE(f_env_type), POINTER :: f_env => NULL()
59 : TYPE(force_env_type), POINTER :: force_env => NULL()
60 : TYPE(cp_subsys_type), POINTER :: subsys => NULL()
61 : TYPE(section_vals_type), POINTER :: root_section => NULL()
62 : TYPE(global_environment_type), POINTER :: globenv => NULL()
63 : INTEGER :: gopt_max_iter = 0
64 : INTEGER :: bump_steps_downwards = 0
65 : INTEGER :: bump_steps_upwards = 0
66 : INTEGER :: md_bumps_max = 0
67 : REAL(KIND=dp) :: fragmentation_threshold = 0.0_dp
68 : INTEGER :: n_atoms = -1
69 : !REAL(KIND=dp) :: adaptive_timestep = 0.0
70 : END TYPE glbopt_worker_type
71 :
72 : CONTAINS
73 :
74 : ! **************************************************************************************************
75 : !> \brief Initializes worker for global optimization
76 : !> \param worker ...
77 : !> \param input_declaration ...
78 : !> \param para_env ...
79 : !> \param root_section ...
80 : !> \param input_path ...
81 : !> \param worker_id ...
82 : !> \param iw ...
83 : !> \author Ole Schuett
84 : ! **************************************************************************************************
85 6 : SUBROUTINE glbopt_worker_init(worker, input_declaration, para_env, root_section, &
86 : input_path, worker_id, iw)
87 : TYPE(glbopt_worker_type), INTENT(INOUT) :: worker
88 : TYPE(section_type), POINTER :: input_declaration
89 : TYPE(mp_para_env_type), POINTER :: para_env
90 : TYPE(section_vals_type), POINTER :: root_section
91 : CHARACTER(LEN=*), INTENT(IN) :: input_path
92 : INTEGER, INTENT(in) :: worker_id, iw
93 :
94 : INTEGER :: i
95 : REAL(kind=dp) :: dist_in_angstrom
96 : TYPE(section_vals_type), POINTER :: glbopt_section
97 :
98 3 : worker%root_section => root_section
99 3 : worker%id = worker_id
100 3 : worker%iw = iw
101 :
102 : ! ======= Create f_env =======
103 : CALL create_force_env(worker%f_env_id, &
104 : input_declaration=input_declaration, &
105 : input_path=input_path, &
106 : input=root_section, &
107 : output_unit=worker%iw, &
108 3 : mpi_comm=para_env)
109 :
110 : ! ======= More setup stuff =======
111 3 : CALL f_env_add_defaults(worker%f_env_id, worker%f_env)
112 3 : worker%force_env => worker%f_env%force_env
113 3 : CALL force_env_get(worker%force_env, globenv=worker%globenv, subsys=worker%subsys)
114 :
115 : ! We want different random-number-streams for each worker
116 6 : DO i = 1, worker_id
117 6 : CALL worker%globenv%gaussian_rng_stream%reset_to_next_substream()
118 : END DO
119 :
120 3 : CALL cp_subsys_get(worker%subsys, natom=worker%n_atoms)
121 :
122 : ! fetch original value from input
123 3 : CALL section_vals_val_get(root_section, "MOTION%GEO_OPT%MAX_ITER", i_val=worker%gopt_max_iter)
124 3 : glbopt_section => section_vals_get_subs_vals(root_section, "SWARM%GLOBAL_OPT")
125 :
126 3 : CALL section_vals_val_get(glbopt_section, "BUMP_STEPS_UPWARDS", i_val=worker%bump_steps_upwards)
127 3 : CALL section_vals_val_get(glbopt_section, "BUMP_STEPS_DOWNWARDS", i_val=worker%bump_steps_downwards)
128 3 : CALL section_vals_val_get(glbopt_section, "MD_BUMPS_MAX", i_val=worker%md_bumps_max)
129 3 : CALL section_vals_val_get(glbopt_section, "FRAGMENTATION_THRESHOLD", r_val=dist_in_angstrom)
130 : !CALL section_vals_val_get(glbopt_section,"MD_ADAPTIVE_TIMESTEP", r_val=worker%adaptive_timestep)
131 3 : worker%fragmentation_threshold = dist_in_angstrom/angstrom
132 3 : END SUBROUTINE glbopt_worker_init
133 :
134 : ! **************************************************************************************************
135 : !> \brief Central execute routine of global optimization worker
136 : !> \param worker ...
137 : !> \param cmd ...
138 : !> \param report ...
139 : !> \author Ole Schuett
140 : ! **************************************************************************************************
141 48 : SUBROUTINE glbopt_worker_execute(worker, cmd, report)
142 : TYPE(glbopt_worker_type), INTENT(INOUT) :: worker
143 : TYPE(swarm_message_type), INTENT(IN) :: cmd
144 : TYPE(swarm_message_type), INTENT(INOUT) :: report
145 :
146 : CHARACTER(len=default_string_length) :: command
147 :
148 48 : CALL swarm_message_get(cmd, "command", command)
149 48 : IF (TRIM(command) == "md_and_gopt") THEN
150 48 : CALL run_mdgopt(worker, cmd, report)
151 : ELSE
152 0 : CPABORT("Worker: received unknown command")
153 : END IF
154 :
155 48 : END SUBROUTINE glbopt_worker_execute
156 :
157 : ! **************************************************************************************************
158 : !> \brief Performs an escape attempt as need by e.g. Minima Hopping
159 : !> \param worker ...
160 : !> \param cmd ...
161 : !> \param report ...
162 : !> \author Ole Schuett
163 : ! **************************************************************************************************
164 48 : SUBROUTINE run_mdgopt(worker, cmd, report)
165 : TYPE(glbopt_worker_type), INTENT(INOUT) :: worker
166 : TYPE(swarm_message_type), INTENT(IN) :: cmd
167 : TYPE(swarm_message_type), INTENT(INOUT) :: report
168 :
169 : INTEGER :: gopt_steps, iframe, md_steps, &
170 : n_fragments, prev_iframe
171 : REAL(kind=dp) :: Epot, temperature
172 48 : REAL(KIND=dp), DIMENSION(:), POINTER :: positions
173 48 : TYPE(glbopt_mdctrl_data_type), TARGET :: mdctrl_data
174 : TYPE(mdctrl_type), POINTER :: mdctrl_p
175 : TYPE(mdctrl_type), TARGET :: mdctrl
176 :
177 48 : NULLIFY (positions)
178 :
179 48 : CALL swarm_message_get(cmd, "temperature", temperature)
180 48 : CALL swarm_message_get(cmd, "iframe", iframe)
181 48 : IF (iframe > 1) THEN
182 46 : CALL swarm_message_get(cmd, "positions", positions)
183 46 : CALL unpack_subsys_particles(worker%subsys, r=positions)
184 : END IF
185 :
186 : ! setup mdctrl callback
187 144 : ALLOCATE (mdctrl_data%epot_history(worker%bump_steps_downwards + worker%bump_steps_upwards + 1))
188 288 : mdctrl_data%epot_history = 0.0
189 48 : mdctrl_data%md_bump_counter = 0
190 48 : mdctrl_data%bump_steps_upwards = worker%bump_steps_upwards
191 48 : mdctrl_data%bump_steps_downwards = worker%bump_steps_downwards
192 48 : mdctrl_data%md_bumps_max = worker%md_bumps_max
193 48 : mdctrl_data%output_unit = worker%iw
194 48 : mdctrl%glbopt => mdctrl_data
195 48 : mdctrl_p => mdctrl
196 :
197 : !IF(worker%adaptive_timestep > 0.0) THEN
198 : ! !TODO: 300K is hard encoded
199 : ! boltz = 1.0 + exp( -temperature * kelvin / 150.0 )
200 : ! timestep = 4.0 * ( boltz - 1.0 ) / boltz / femtoseconds
201 : ! !timestep = 0.01_dp / femtoseconds
202 : ! !timestep = SQRT(MIN(0.5, 2.0/(1+exp(-300.0/(temperature*kelvin))))) / femtoseconds
203 : ! CALL section_vals_val_set(worker%root_section, "MOTION%MD%TIMESTEP", r_val=timestep)
204 : ! IF(worker%iw>0)&
205 : ! WRITE (worker%iw,'(A,35X,F20.3)') ' GLBOPT| MD timestep [fs]',timestep*femtoseconds
206 : !ENDIF
207 :
208 48 : prev_iframe = iframe
209 48 : IF (iframe == 0) iframe = 1 ! qs_mol_dyn behaves differently for STEP_START_VAL=0
210 48 : CALL section_vals_val_set(worker%root_section, "MOTION%MD%STEP_START_VAL", i_val=iframe - 1)
211 48 : CALL section_vals_val_set(worker%root_section, "MOTION%MD%TEMPERATURE", r_val=temperature)
212 :
213 48 : IF (worker%iw > 0) THEN
214 48 : WRITE (worker%iw, '(A,33X,F20.3)') ' GLBOPT| MD temperature [K]', temperature*kelvin
215 48 : WRITE (worker%iw, '(A,29X,I10)') " GLBOPT| Starting MD at trajectory frame ", iframe
216 : END IF
217 :
218 : ! run MD
219 48 : CALL qs_mol_dyn(worker%force_env, worker%globenv, mdctrl=mdctrl_p)
220 :
221 48 : iframe = mdctrl_data%itimes + 1
222 48 : md_steps = iframe - prev_iframe
223 48 : IF (worker%iw > 0) WRITE (worker%iw, '(A,I4,A)') " GLBOPT| md ended after ", md_steps, " steps."
224 :
225 : ! fix fragmentation
226 52 : IF (.NOT. ASSOCIATED(positions)) ALLOCATE (positions(3*worker%n_atoms))
227 48 : CALL pack_subsys_particles(worker%subsys, r=positions)
228 48 : n_fragments = 0
229 : DO
230 48 : n_fragments = n_fragments + 1
231 48 : IF (fix_fragmentation(positions, worker%fragmentation_threshold)) EXIT
232 : END DO
233 48 : CALL unpack_subsys_particles(worker%subsys, r=positions)
234 :
235 48 : IF (n_fragments > 0 .AND. worker%iw > 0) &
236 48 : WRITE (worker%iw, '(A,13X,I10)') " GLBOPT| Ran fix_fragmentation times:", n_fragments
237 :
238 : ! setup geometry optimization
239 48 : IF (worker%iw > 0) WRITE (worker%iw, '(A,13X,I10)') " GLBOPT| Starting local optimisation at trajectory frame ", iframe
240 48 : CALL section_vals_val_set(worker%root_section, "MOTION%GEO_OPT%STEP_START_VAL", i_val=iframe - 1)
241 : CALL section_vals_val_set(worker%root_section, "MOTION%GEO_OPT%MAX_ITER", &
242 48 : i_val=iframe + worker%gopt_max_iter)
243 :
244 : ! run geometry optimization
245 48 : CALL cp_geo_opt(worker%force_env, worker%globenv, rm_restart_info=.FALSE.)
246 :
247 48 : prev_iframe = iframe
248 48 : CALL section_vals_val_get(worker%root_section, "MOTION%GEO_OPT%STEP_START_VAL", i_val=iframe)
249 48 : iframe = iframe + 2 ! Compensates for different START_VAL interpretation.
250 48 : gopt_steps = iframe - prev_iframe - 1
251 48 : IF (worker%iw > 0) WRITE (worker%iw, '(A,I4,A)') " GLBOPT| gopt ended after ", gopt_steps, " steps."
252 48 : CALL force_env_get(worker%force_env, potential_energy=Epot)
253 48 : IF (worker%iw > 0) WRITE (worker%iw, '(A,25X,E20.10)') ' GLBOPT| Potential Energy [Hartree]', Epot
254 :
255 : ! assemble report
256 48 : CALL swarm_message_add(report, "Epot", Epot)
257 48 : CALL swarm_message_add(report, "iframe", iframe)
258 48 : CALL swarm_message_add(report, "md_steps", md_steps)
259 48 : CALL swarm_message_add(report, "gopt_steps", gopt_steps)
260 48 : CALL pack_subsys_particles(worker%subsys, r=positions)
261 48 : CALL swarm_message_add(report, "positions", positions)
262 :
263 48 : DEALLOCATE (positions)
264 192 : END SUBROUTINE run_mdgopt
265 :
266 : ! **************************************************************************************************
267 : !> \brief Helper routine for run_mdgopt, fixes a fragmented atomic cluster.
268 : !> \param positions ...
269 : !> \param bondlength ...
270 : !> \return ...
271 : !> \author Stefan Goedecker
272 : ! **************************************************************************************************
273 48 : FUNCTION fix_fragmentation(positions, bondlength) RESULT(all_connected)
274 : REAL(KIND=dp), DIMENSION(:) :: positions
275 : REAL(KIND=dp) :: bondlength
276 : LOGICAL :: all_connected
277 :
278 : INTEGER :: cluster_edge, fragment_edge, i, j, &
279 : n_particles, stack_size
280 48 : INTEGER, ALLOCATABLE, DIMENSION(:) :: stack
281 48 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: marked
282 : REAL(KIND=dp) :: d, dr(3), min_dist, s
283 :
284 48 : n_particles = SIZE(positions)/3
285 192 : ALLOCATE (stack(n_particles), marked(n_particles))
286 :
287 528 : marked = .FALSE.; stack_size = 0
288 :
289 : ! First particle taken as root of flooding, mark it and push to stack
290 48 : marked(1) = .TRUE.; stack(1) = 1; stack_size = 1
291 :
292 528 : DO WHILE (stack_size > 0)
293 480 : i = stack(stack_size); stack_size = stack_size - 1 !pop
294 5328 : DO j = 1, n_particles
295 5280 : IF (norm(diff(positions, i, j)) < 1.25*bondlength) THEN ! they are close = they are connected
296 9498 : IF (.NOT. marked(j)) THEN
297 432 : marked(j) = .TRUE.
298 432 : stack(stack_size + 1) = j; stack_size = stack_size + 1; !push
299 : END IF
300 : END IF
301 : END DO
302 : END DO
303 :
304 528 : all_connected = ALL(marked) !did we visit every particle?
305 48 : IF (all_connected) RETURN
306 :
307 : ! make sure we keep the larger chunk
308 0 : IF (COUNT(marked) < n_particles/2) marked(:) = .NOT. (marked(:))
309 :
310 0 : min_dist = HUGE(1.0)
311 0 : cluster_edge = -1
312 0 : fragment_edge = -1
313 0 : DO i = 1, n_particles
314 0 : IF (marked(i)) CYCLE
315 0 : DO j = 1, n_particles
316 0 : IF (.NOT. marked(j)) CYCLE
317 0 : d = norm(diff(positions, i, j))
318 0 : IF (d < min_dist) THEN
319 0 : min_dist = d
320 0 : cluster_edge = i
321 0 : fragment_edge = j
322 : END IF
323 : END DO
324 : END DO
325 :
326 0 : dr = diff(positions, cluster_edge, fragment_edge)
327 0 : s = 1.0 - bondlength/norm(dr)
328 0 : DO i = 1, n_particles
329 0 : IF (marked(i)) CYCLE
330 0 : positions(3*i - 2:3*i) = positions(3*i - 2:3*i) - s*dr
331 : END DO
332 :
333 48 : END FUNCTION fix_fragmentation
334 :
335 : ! **************************************************************************************************
336 : !> \brief Helper routine for fix_fragmentation, calculates atomic distance
337 : !> \param positions ...
338 : !> \param i ...
339 : !> \param j ...
340 : !> \return ...
341 : !> \author Ole Schuett
342 : ! **************************************************************************************************
343 4800 : PURE FUNCTION diff(positions, i, j) RESULT(dr)
344 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: positions
345 : INTEGER, INTENT(IN) :: i, j
346 : REAL(KIND=dp), DIMENSION(3) :: dr
347 :
348 19200 : dr = positions(3*i - 2:3*i) - positions(3*j - 2:3*j)
349 4800 : END FUNCTION diff
350 :
351 : ! **************************************************************************************************
352 : !> \brief Helper routine for fix_fragmentation, calculates vector norm
353 : !> \param vec ...
354 : !> \return ...
355 : !> \author Ole Schuett
356 : ! **************************************************************************************************
357 4800 : PURE FUNCTION norm(vec) RESULT(res)
358 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: vec
359 : REAL(KIND=dp) :: res
360 :
361 19200 : res = SQRT(DOT_PRODUCT(vec, vec))
362 4800 : END FUNCTION norm
363 :
364 : ! **************************************************************************************************
365 : !> \brief Finalizes worker for global optimization
366 : !> \param worker ...
367 : !> \author Ole Schuett
368 : ! **************************************************************************************************
369 6 : SUBROUTINE glbopt_worker_finalize(worker)
370 : TYPE(glbopt_worker_type), INTENT(INOUT) :: worker
371 :
372 : INTEGER :: ierr
373 :
374 3 : CALL f_env_rm_defaults(worker%f_env)
375 3 : CALL destroy_force_env(worker%f_env_id, ierr)
376 3 : IF (ierr /= 0) CPABORT("destroy_force_env failed")
377 3 : END SUBROUTINE glbopt_worker_finalize
378 :
379 0 : END MODULE glbopt_worker
|