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 Routines for the Minima Crawling global optimization scheme
10 : !> \author Ole Schuett
11 : ! **************************************************************************************************
12 : MODULE glbopt_mincrawl
13 : USE cp_log_handling, ONLY: cp_get_default_logger,&
14 : cp_logger_type
15 : USE cp_output_handling, ONLY: cp_print_key_finished_output,&
16 : cp_print_key_unit_nr
17 : USE cp_units, ONLY: cp_unit_from_cp2k
18 : USE glbopt_history, ONLY: history_add,&
19 : history_finalize,&
20 : history_fingerprint,&
21 : history_fingerprint_type,&
22 : history_init,&
23 : history_lookup,&
24 : history_type
25 : USE input_constants, ONLY: dump_xmol
26 : USE input_section_types, ONLY: section_vals_get_subs_vals,&
27 : section_vals_type,&
28 : section_vals_val_get
29 : USE kinds, ONLY: default_string_length,&
30 : dp
31 : USE parallel_rng_types, ONLY: rng_stream_type
32 : USE particle_methods, ONLY: write_particle_coordinates
33 : USE particle_types, ONLY: particle_type
34 : USE physcon, ONLY: kelvin
35 : USE swarm_message, ONLY: swarm_message_add,&
36 : swarm_message_get,&
37 : swarm_message_type
38 : #include "../base/base_uses.f90"
39 :
40 : IMPLICIT NONE
41 : PRIVATE
42 :
43 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'glbopt_mincrawl'
44 :
45 : PUBLIC :: mincrawl_type
46 : PUBLIC :: mincrawl_init, mincrawl_finalize
47 : PUBLIC :: mincrawl_steer
48 :
49 : TYPE minima_type
50 : INTEGER :: id = -1
51 : REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: pos
52 : REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: escape_hist
53 : REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: tempdist
54 : REAL(KIND=dp) :: Epot = -1.0
55 : TYPE(history_fingerprint_type) :: fp
56 : LOGICAL :: disabled = .FALSE.
57 : INTEGER :: n_active = 0
58 : INTEGER :: n_sampled = 0
59 : END TYPE minima_type
60 :
61 : TYPE minima_p_type
62 : TYPE(minima_type), POINTER :: p => Null()
63 : END TYPE minima_p_type
64 :
65 : TYPE worker_state_type
66 : TYPE(minima_type), POINTER :: start_minima => Null()
67 : INTEGER :: tempstep = 0
68 : INTEGER :: iframe = 1
69 : END TYPE worker_state_type
70 :
71 : TYPE mincrawl_type
72 : PRIVATE
73 : TYPE(history_type) :: history
74 : TYPE(worker_state_type), DIMENSION(:), ALLOCATABLE :: workers
75 : TYPE(minima_p_type), DIMENSION(:), ALLOCATABLE :: minimas
76 : REAL(KIND=dp) :: tempstep_base = 0
77 : INTEGER :: tempstep_max = 0
78 : REAL(KIND=dp) :: tempdist_init_width = 0
79 : REAL(KIND=dp) :: tempdist_update_width = 0
80 : REAL(KIND=dp) :: tempdist_update_height = 0
81 : INTEGER :: esc_hist_len = 0
82 : INTEGER :: tempstep_init = 0
83 : REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: tempdist_init
84 : INTEGER :: n_minima = 0
85 : INTEGER :: n_workers = 0
86 : INTEGER :: worker_per_min = 0
87 : INTEGER :: iw = 0
88 : INTEGER :: minima_traj_unit = 0
89 : TYPE(section_vals_type), POINTER :: mincrawl_section => Null()
90 : TYPE(rng_stream_type) :: rng_stream
91 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set => Null()
92 : END TYPE mincrawl_type
93 :
94 : CONTAINS
95 :
96 : ! **************************************************************************************************
97 : !> \brief Initializes master for Minima Crawling
98 : !> \param this ...
99 : !> \param glbopt_section ...
100 : !> \param n_workers ...
101 : !> \param iw ...
102 : !> \param particle_set ...
103 : !> \author Ole Schuett
104 : ! **************************************************************************************************
105 1 : SUBROUTINE mincrawl_init(this, glbopt_section, n_workers, iw, particle_set)
106 : TYPE(mincrawl_type) :: this
107 : TYPE(section_vals_type), POINTER :: glbopt_section
108 : INTEGER, INTENT(IN) :: n_workers, iw
109 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
110 :
111 : INTEGER :: i
112 : REAL(kind=dp) :: temp_in_kelvin
113 : TYPE(cp_logger_type), POINTER :: logger
114 : TYPE(section_vals_type), POINTER :: history_section
115 :
116 1 : NULLIFY (logger, history_section)
117 :
118 : ! read input
119 1 : this%mincrawl_section => section_vals_get_subs_vals(glbopt_section, "MINIMA_CRAWLING")
120 1 : CALL section_vals_val_get(this%mincrawl_section, "TEMPSTEP_BASE", r_val=this%tempstep_base)
121 1 : CALL section_vals_val_get(this%mincrawl_section, "TEMPSTEP_MAX", i_val=this%tempstep_max)
122 1 : CALL section_vals_val_get(this%mincrawl_section, "TEMPDIST_INIT_WIDTH", r_val=this%tempdist_init_width)
123 1 : CALL section_vals_val_get(this%mincrawl_section, "TEMPDIST_UPDATE_WIDTH", r_val=this%tempdist_update_width)
124 1 : CALL section_vals_val_get(this%mincrawl_section, "TEMPDIST_UPDATE_HEIGHT", r_val=this%tempdist_update_height)
125 1 : CALL section_vals_val_get(this%mincrawl_section, "TEMPERATURE_INIT", r_val=temp_in_kelvin)
126 1 : this%tempstep_init = temp2tempstep(this, temp_in_kelvin/kelvin)
127 1 : CALL section_vals_val_get(this%mincrawl_section, "WORKER_PER_MINIMA", i_val=this%worker_per_min)
128 1 : CALL section_vals_val_get(this%mincrawl_section, "ESCAPE_HISTORY_LENGTH", i_val=this%esc_hist_len)
129 :
130 : !init minima trajectory
131 1 : logger => cp_get_default_logger()
132 : this%minima_traj_unit = cp_print_key_unit_nr(logger, &
133 : this%mincrawl_section, "MINIMA_TRAJECTORY", &
134 : middle_name="minima", extension=".xyz", &
135 1 : file_action="WRITE", file_position="REWIND")
136 :
137 : !init history
138 1 : history_section => section_vals_get_subs_vals(glbopt_section, "HISTORY")
139 1 : CALL history_init(this%history, history_section, iw=iw)
140 :
141 : !allocate data structures
142 1001 : ALLOCATE (this%minimas(1000)) !will be grown if needed
143 :
144 4 : ALLOCATE (this%workers(n_workers))
145 1 : this%n_workers = n_workers
146 1 : this%iw = iw
147 1 : this%particle_set => particle_set
148 :
149 : ! call fermi-like stepfunction for initial temp-dist
150 3 : ALLOCATE (this%tempdist_init(this%tempstep_max))
151 101 : this%tempdist_init = 0.0
152 101 : DO i = 1, this%tempstep_max
153 101 : this%tempdist_init(i) = 1.0/(1.0 + EXP((this%tempstep_init - i)/this%tempdist_init_width))
154 : END DO
155 :
156 1 : this%rng_stream = rng_stream_type(name="mincrawl")
157 1 : END SUBROUTINE mincrawl_init
158 :
159 : ! **************************************************************************************************
160 : !> \brief Central steering routine of Minima Crawling
161 : !> \param this ...
162 : !> \param report ...
163 : !> \param cmd ...
164 : !> \author Ole Schuett
165 : ! **************************************************************************************************
166 82 : SUBROUTINE mincrawl_steer(this, report, cmd)
167 : TYPE(mincrawl_type) :: this
168 : TYPE(swarm_message_type) :: report, cmd
169 :
170 : CHARACTER(len=default_string_length) :: status
171 : INTEGER :: wid
172 : TYPE(minima_type), POINTER :: best_minima
173 :
174 41 : CALL swarm_message_get(report, "status", status)
175 41 : CALL swarm_message_get(report, "worker_id", wid)
176 :
177 41 : IF (TRIM(status) == "initial_hello") THEN
178 1 : this%workers(wid)%tempstep = this%tempstep_init
179 1 : CALL swarm_message_add(cmd, "command", "md_and_gopt")
180 1 : CALL swarm_message_add(cmd, "iframe", 1)
181 1 : CALL swarm_message_add(cmd, "temperature", tempstep2temp(this, this%workers(wid)%tempstep))
182 1 : RETURN
183 : END IF
184 :
185 40 : IF (TRIM(status) == "ok") &
186 40 : CALL mincrawl_register_minima(this, report)
187 :
188 : IF (.FALSE.) CALL print_tempdist(best_minima)
189 :
190 40 : best_minima => choose_promising_minima(this)
191 :
192 40 : IF (.NOT. ASSOCIATED(best_minima)) THEN ! no suitable minima found
193 0 : CALL swarm_message_add(cmd, "command", "wait")
194 : !WRITE(this%iw,*) " MINCRAWL| Waiting until new minima become available"
195 0 : RETURN
196 : END IF
197 :
198 40 : best_minima%n_active = best_minima%n_active + 1
199 40 : best_minima%n_sampled = best_minima%n_sampled + 1
200 40 : this%workers(wid)%start_minima => best_minima
201 40 : this%workers(wid)%tempstep = choose_tempstep(this, best_minima)
202 :
203 40 : CALL swarm_message_add(cmd, "command", "md_and_gopt")
204 40 : CALL swarm_message_add(cmd, "iframe", this%workers(wid)%iframe)
205 40 : CALL swarm_message_add(cmd, "temperature", tempstep2temp(this, this%workers(wid)%tempstep))
206 40 : CALL swarm_message_add(cmd, "positions", best_minima%pos)
207 :
208 40 : IF (this%iw > 0) THEN
209 : WRITE (this%iw, '(1X,A,T71,I10)') &
210 40 : "MINCRAWL| Total number of found minima", this%n_minima
211 : WRITE (this%iw, '(1X,A,T71,I10)') &
212 40 : "MINCRAWL| Sampling minima with id", best_minima%id
213 : WRITE (this%iw, '(1X,A,I10,A,A,T71,F10.3)') &
214 40 : "MINCRAWL| Temperature (step ", this%workers(wid)%tempstep, " ) ", &
215 80 : "[Kelvin]", kelvin*tempstep2temp(this, this%workers(wid)%tempstep)
216 : END IF
217 :
218 : END SUBROUTINE mincrawl_steer
219 :
220 : ! **************************************************************************************************
221 : !> \brief Helper routine for mincrawl_steer, choses minimum based on its score.
222 : !> \param this ...
223 : !> \return ...
224 : !> \author Ole Schuett
225 : ! **************************************************************************************************
226 40 : FUNCTION choose_promising_minima(this) RESULT(minima)
227 : TYPE(mincrawl_type) :: this
228 : TYPE(minima_type), POINTER :: minima
229 :
230 : INTEGER :: i
231 : REAL(KIND=dp) :: score, score_best
232 :
233 40 : score_best = HUGE(1.0)
234 40 : NULLIFY (minima)
235 :
236 342 : DO i = 1, this%n_minima
237 302 : IF (this%minimas(i)%p%disabled) CYCLE
238 160 : IF (this%minimas(i)%p%n_active > this%worker_per_min) CYCLE
239 160 : score = minima_score(this%minimas(i)%p)
240 : ! WRITE (*,*) "Minima: ", i, " active: ",this%minimas(i)%active, " E_expect: ", E_expect
241 200 : IF (score < score_best) THEN
242 75 : score_best = score
243 75 : minima => this%minimas(i)%p
244 : END IF
245 : END DO
246 40 : END FUNCTION choose_promising_minima
247 :
248 : ! **************************************************************************************************
249 : !> \brief Helper routine for choose_promising_minima, calculates a minimum's score
250 : !> \param minima ...
251 : !> \return ...
252 : !> \author Ole Schuett
253 : ! **************************************************************************************************
254 160 : FUNCTION minima_score(minima) RESULT(res)
255 : TYPE(minima_type), POINTER :: minima
256 : REAL(KIND=dp) :: res
257 :
258 1760 : res = SUM(minima%escape_hist)/SIZE(minima%escape_hist)
259 160 : END FUNCTION minima_score
260 :
261 : ! **************************************************************************************************
262 : !> \brief Helper routine for mincrawl_steer, samples from a temp-dist.
263 : !> \param this ...
264 : !> \param minima ...
265 : !> \return ...
266 : !> \author Ole Schuett
267 : ! **************************************************************************************************
268 40 : FUNCTION choose_tempstep(this, minima) RESULT(step)
269 : TYPE(mincrawl_type) :: this
270 : TYPE(minima_type), POINTER :: minima
271 : INTEGER :: step
272 :
273 : REAL(KIND=dp) :: a, r
274 :
275 : DO
276 345 : r = this%rng_stream%next()
277 345 : step = INT(r*SIZE(minima%tempdist)) + 1
278 345 : a = 1.0 - 2.0*ABS(minima%tempdist(step) - 0.5)
279 345 : r = this%rng_stream%next()
280 345 : IF (r < a) EXIT
281 : END DO
282 :
283 40 : END FUNCTION choose_tempstep
284 :
285 : ! **************************************************************************************************
286 : !> \brief Debugging routine, prints a minimum's temp-distribution.
287 : !> \param minima ...
288 : !> \author Ole Schuett
289 : ! **************************************************************************************************
290 0 : SUBROUTINE print_tempdist(minima)
291 : TYPE(minima_type), POINTER :: minima
292 :
293 : INTEGER :: i
294 :
295 : !WRITE (*,*) "tempdist: ", SUM(minima%tempdist, DIM=1)
296 :
297 0 : DO i = 1, SIZE(minima%tempdist)
298 0 : WRITE (*, *) "tempstep: ", i, minima%tempdist(i)
299 : END DO
300 0 : END SUBROUTINE print_tempdist
301 :
302 : ! **************************************************************************************************
303 : !> \brief Helper routine, convertes a discrete temp-step to a temperature.
304 : !> \param this ...
305 : !> \param step ...
306 : !> \return ...
307 : !> \author Ole Schuett
308 : ! **************************************************************************************************
309 81 : FUNCTION tempstep2temp(this, step) RESULT(temp_in_au)
310 : TYPE(mincrawl_type) :: this
311 : INTEGER :: step
312 : REAL(KIND=dp) :: temp_in_au
313 :
314 81 : temp_in_au = (this%tempstep_base**step)/kelvin
315 81 : END FUNCTION tempstep2temp
316 :
317 : ! **************************************************************************************************
318 : !> \brief Helper routine, convertes a temperature to a discrete temp-step.
319 : !> \param this ...
320 : !> \param temp_in_au ...
321 : !> \return ...
322 : !> \author Ole Schuett
323 : ! **************************************************************************************************
324 1 : FUNCTION temp2tempstep(this, temp_in_au) RESULT(step)
325 : TYPE(mincrawl_type) :: this
326 : REAL(KIND=dp) :: temp_in_au
327 : INTEGER :: step
328 :
329 1 : step = INT(LOG(temp_in_au*kelvin)/LOG(this%tempstep_base))
330 : !WRITE(*,*) "temp: ", temp_in_au*kelvin, this%tempstep_base
331 : !WRITE(*,*) "step: ", step
332 1 : IF (step > this%tempstep_max) CPABORT("temp2tempstep: step > tempstep_max")
333 1 : END FUNCTION temp2tempstep
334 :
335 : ! **************************************************************************************************
336 : !> \brief Helper routine for mincrawl_steer
337 : !> Incorporates information of new report into history.
338 : !> \param this ...
339 : !> \param report ...
340 : !> \author Ole Schuett
341 : ! **************************************************************************************************
342 40 : SUBROUTINE mincrawl_register_minima(this, report)
343 : TYPE(mincrawl_type) :: this
344 : TYPE(swarm_message_type) :: report
345 :
346 : INTEGER :: new_mid, tempstep, wid
347 : LOGICAL :: minima_known
348 : REAL(KIND=dp) :: report_Epot
349 40 : REAL(KIND=dp), DIMENSION(:), POINTER :: report_positions
350 : TYPE(history_fingerprint_type) :: report_fp
351 40 : TYPE(minima_p_type), ALLOCATABLE, DIMENSION(:) :: minimas_tmp
352 : TYPE(minima_type), POINTER :: new_minima, start_minima
353 :
354 40 : NULLIFY (start_minima, new_minima, report_positions)
355 :
356 40 : CALL swarm_message_get(report, "worker_id", wid)
357 40 : CALL swarm_message_get(report, "Epot", report_Epot)
358 40 : CALL swarm_message_get(report, "positions", report_positions)
359 40 : CALL swarm_message_get(report, "iframe", this%workers(wid)%iframe)
360 :
361 40 : start_minima => this%workers(wid)%start_minima
362 40 : tempstep = this%workers(wid)%tempstep
363 :
364 40 : report_fp = history_fingerprint(report_Epot, report_positions)
365 40 : CALL history_lookup(this%history, report_fp, minima_known)
366 :
367 40 : IF (ASSOCIATED(start_minima)) THEN
368 39 : start_minima%n_active = start_minima%n_active - 1
369 39 : IF (start_minima%n_active < 0) CPABORT("negative n_active")
370 :
371 : ! update tempdist and escape_hist
372 39 : IF (minima_known) THEN
373 29 : CALL update_tempdist(this, start_minima%tempdist, tempstep, -1)
374 : ELSE
375 10 : CALL update_tempdist(this, start_minima%tempdist, tempstep, +1)
376 110 : start_minima%escape_hist(:) = EOSHIFT(start_minima%escape_hist, 1)
377 10 : start_minima%escape_hist(1) = report_Epot
378 : END IF
379 :
380 : END IF
381 :
382 40 : IF (.NOT. minima_known) THEN
383 11 : this%n_minima = this%n_minima + 1
384 11 : IF (this%n_minima > SIZE(this%minimas)) THEN
385 0 : ALLOCATE (minimas_tmp(SIZE(this%minimas)))
386 0 : minimas_tmp(:) = this%minimas
387 0 : DEALLOCATE (this%minimas)
388 0 : ALLOCATE (this%minimas(SIZE(minimas_tmp) + 1000))
389 0 : this%minimas(:SIZE(minimas_tmp)) = minimas_tmp
390 0 : DEALLOCATE (minimas_tmp)
391 : END IF
392 :
393 11 : new_mid = this%n_minima
394 11 : ALLOCATE (this%minimas(new_mid)%p)
395 11 : new_minima => this%minimas(new_mid)%p
396 11 : new_minima%id = new_mid
397 33 : ALLOCATE (new_minima%escape_hist(this%esc_hist_len))
398 33 : ALLOCATE (new_minima%tempdist(this%tempstep_max))
399 :
400 121 : new_minima%escape_hist(:) = report_Epot !init with Epot
401 :
402 11 : IF (ASSOCIATED(start_minima)) THEN
403 2020 : new_minima%tempdist(:) = start_minima%tempdist ! inherit tempdist
404 : ELSE
405 101 : new_minima%tempdist(:) = this%tempdist_init
406 : END IF
407 :
408 11 : new_minima%Epot = report_Epot
409 11 : new_minima%fp = report_fp
410 33 : ALLOCATE (new_minima%pos(SIZE(report_positions)))
411 682 : new_minima%pos(:) = report_positions
412 :
413 11 : IF (ASSOCIATED(start_minima)) THEN
414 10 : IF (report_Epot < start_minima%Epot) THEN
415 6 : start_minima%disabled = .TRUE.
416 6 : IF (this%iw > 0) WRITE (this%iw, '(1X,A,T71,I10)') &
417 6 : "MINCRAWL| Disabling minimum with id", start_minima%id
418 : END IF
419 : END IF
420 :
421 11 : IF (this%iw > 0) WRITE (this%iw, '(1X,A,T71,I10)') &
422 11 : "MINCRAWL| Adding new minima with id", new_mid
423 :
424 11 : CALL history_add(this%history, report_fp, id=new_mid)
425 11 : CALL write_minima_traj(this, wid, new_mid, report_Epot, report_positions)
426 : END IF
427 40 : DEALLOCATE (report_positions)
428 80 : END SUBROUTINE mincrawl_register_minima
429 :
430 : ! **************************************************************************************************
431 : !> \brief Helper routine for mincrawl_register_minima.
432 : !> Adds or subtracts small Gaussian from a minimum's temp-distribution.
433 : !> \param this ...
434 : !> \param tempdist ...
435 : !> \param center ...
436 : !> \param direction ...
437 : !> \author Ole Schuett
438 : ! **************************************************************************************************
439 39 : SUBROUTINE update_tempdist(this, tempdist, center, direction)
440 : TYPE(mincrawl_type) :: this
441 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: tempdist
442 : INTEGER :: center, direction
443 :
444 : INTEGER :: i
445 :
446 3939 : DO i = 1, SIZE(tempdist)
447 : tempdist(i) = tempdist(i) + this%tempdist_update_height &
448 3900 : *REAL(direction, KIND=dp)*EXP(-((center - i)/this%tempdist_update_width)**2)
449 3939 : tempdist(i) = MAX(0.0_dp, MIN(1.0_dp, tempdist(i)))
450 : END DO
451 39 : END SUBROUTINE update_tempdist
452 :
453 : ! **************************************************************************************************
454 : !> \brief Helper routine for mincrawl_register_minima, write trajectory.
455 : !> \param this ...
456 : !> \param worker_id ...
457 : !> \param minimum_id ...
458 : !> \param Epot ...
459 : !> \param positions ...
460 : !> \author Ole Schuett
461 : ! **************************************************************************************************
462 11 : SUBROUTINE write_minima_traj(this, worker_id, minimum_id, Epot, positions)
463 : TYPE(mincrawl_type), INTENT(INOUT) :: this
464 : INTEGER, INTENT(IN) :: worker_id, minimum_id
465 : REAL(KIND=dp), INTENT(IN) :: Epot
466 : REAL(KIND=dp), DIMENSION(:), POINTER :: positions
467 :
468 : CHARACTER(len=default_string_length) :: title, unit_str
469 : REAL(KIND=dp) :: unit_conv
470 :
471 11 : IF (this%minima_traj_unit <= 0) RETURN
472 :
473 11 : WRITE (title, '(A,I8,A,I5,A,F20.10)') 'minimum_id = ', minimum_id, &
474 22 : ' worker_id = ', worker_id, ' Epot = ', Epot
475 :
476 : !get the conversion factor for the length unit
477 : CALL section_vals_val_get(this%mincrawl_section, "MINIMA_TRAJECTORY%UNIT", &
478 11 : c_val=unit_str)
479 11 : unit_conv = cp_unit_from_cp2k(1.0_dp, TRIM(unit_str))
480 :
481 : CALL write_particle_coordinates(this%particle_set, &
482 : iunit=this%minima_traj_unit, &
483 : output_format=dump_xmol, &
484 : content="POS", &
485 : title=TRIM(title), &
486 : array=positions, &
487 11 : unit_conv=unit_conv)
488 : END SUBROUTINE write_minima_traj
489 :
490 : ! **************************************************************************************************
491 : !> \brief Finalizes master for Minima Crawling
492 : !> \param this ...
493 : !> \author Ole Schuett
494 : ! **************************************************************************************************
495 1 : SUBROUTINE mincrawl_finalize(this)
496 : TYPE(mincrawl_type) :: this
497 :
498 : INTEGER :: i
499 : TYPE(cp_logger_type), POINTER :: logger
500 :
501 1 : NULLIFY (logger)
502 :
503 12 : DO i = 1, this%n_minima
504 : !WRITE (*,*) "Minima: ", i, " n_sampled: ",this%minimas(i)%n_sampled
505 12 : DEALLOCATE (this%minimas(i)%p)
506 : END DO
507 :
508 1 : logger => cp_get_default_logger()
509 : CALL cp_print_key_finished_output(this%minima_traj_unit, logger, &
510 1 : this%mincrawl_section, "MINIMA_TRAJECTORY")
511 :
512 1 : CALL history_finalize(this%history)
513 1 : END SUBROUTINE mincrawl_finalize
514 :
515 0 : END MODULE glbopt_mincrawl
516 :
|