Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief Routines for Geometry optimization using BFGS algorithm
10 : !> \par History
11 : !> Module modified by Pierre-André Cazade [pcazade] 01.2020 - University of Limerick.
12 : !> Modifications enable Space Group Symmetry.
13 : ! **************************************************************************************************
14 : MODULE bfgs_optimizer
15 :
16 : USE atomic_kind_list_types, ONLY: atomic_kind_list_type
17 : USE atomic_kind_types, ONLY: get_atomic_kind,&
18 : get_atomic_kind_set
19 : USE bibliography, ONLY: Lindh1995,&
20 : cite_reference
21 : USE cell_opt_utils, ONLY: apply_cell_constraints
22 : USE cell_types, ONLY: cell_type,&
23 : pbc
24 : USE constraint_fxd, ONLY: fix_atom_control
25 : USE cp_blacs_env, ONLY: cp_blacs_env_create,&
26 : cp_blacs_env_release,&
27 : cp_blacs_env_type
28 : USE cp_external_control, ONLY: external_control
29 : USE cp_files, ONLY: close_file,&
30 : open_file
31 : USE cp_fm_basic_linalg, ONLY: cp_fm_column_scale,&
32 : cp_fm_matvec,&
33 : cp_fm_transpose
34 : USE cp_fm_diag, ONLY: choose_eigv_solver
35 : USE cp_fm_struct, ONLY: cp_fm_struct_create,&
36 : cp_fm_struct_release,&
37 : cp_fm_struct_type
38 : USE cp_fm_types, ONLY: cp_fm_create,&
39 : cp_fm_get_info,&
40 : cp_fm_read_unformatted,&
41 : cp_fm_release,&
42 : cp_fm_set_all,&
43 : cp_fm_to_fm,&
44 : cp_fm_type,&
45 : cp_fm_write_unformatted
46 : USE cp_log_handling, ONLY: cp_get_default_logger,&
47 : cp_logger_type,&
48 : cp_to_string
49 : USE cp_output_handling, ONLY: cp_iterate,&
50 : cp_p_file,&
51 : cp_print_key_finished_output,&
52 : cp_print_key_should_output,&
53 : cp_print_key_unit_nr
54 : USE cp_subsys_types, ONLY: cp_subsys_get,&
55 : cp_subsys_type
56 : USE force_env_types, ONLY: force_env_get,&
57 : force_env_type
58 : USE global_types, ONLY: global_environment_type
59 : USE gopt_f_methods, ONLY: cp_eval_at,&
60 : gopt_f_ii,&
61 : gopt_f_io,&
62 : gopt_f_io_finalize,&
63 : gopt_f_io_init,&
64 : print_geo_opt_header,&
65 : print_geo_opt_nc
66 : USE gopt_f_types, ONLY: gopt_f_type
67 : USE gopt_param_types, ONLY: gopt_param_type
68 : USE ieee_arithmetic, ONLY: ieee_is_finite
69 : USE input_constants, ONLY: bfgs_direct,&
70 : bfgs_inverse,&
71 : default_cell_method_id,&
72 : default_ts_method_id
73 : USE input_section_types, ONLY: section_vals_get_subs_vals,&
74 : section_vals_type,&
75 : section_vals_val_get,&
76 : section_vals_val_set
77 : USE iso_fortran_env, ONLY: iostat_end
78 : USE kinds, ONLY: default_path_length,&
79 : dp
80 : USE machine, ONLY: m_flush,&
81 : m_walltime
82 : USE mathlib, ONLY: det_3x3,&
83 : inv_3x3
84 : USE message_passing, ONLY: mp_para_env_type
85 : USE parallel_gemm_api, ONLY: parallel_gemm
86 : USE particle_list_types, ONLY: particle_list_type
87 : USE space_groups, ONLY: identify_space_group,&
88 : print_spgr,&
89 : spgr_apply_rotations_coord,&
90 : spgr_apply_rotations_force
91 : USE space_groups_types, ONLY: spgr_type
92 : #include "../base/base_uses.f90"
93 :
94 : IMPLICIT NONE
95 : PRIVATE
96 :
97 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'bfgs_optimizer'
98 : LOGICAL, PARAMETER :: debug_this_module = .TRUE.
99 :
100 : ! Both matrix representations share one restart format and the existing file name.
101 : CHARACTER(LEN=16), PARAMETER :: bfgs_restart_magic = "CP2K BFGS STATE "
102 : INTEGER, PARAMETER :: bfgs_restart_version = 1
103 : REAL(KIND=dp), PARAMETER :: min_hessian_eigenvalue = 1.0e-4_dp
104 :
105 : PUBLIC :: bfgs_read_restart_header, geoopt_bfgs
106 :
107 : CONTAINS
108 :
109 : ! **************************************************************************************************
110 : !> \brief Main driver for BFGS geometry optimizations
111 : !> \param force_env ...
112 : !> \param gopt_param ...
113 : !> \param globenv ...
114 : !> \param geo_section ...
115 : !> \param gopt_env ...
116 : !> \param x0 ...
117 : !> \par History
118 : !> 01.2020 modified to perform Space Group Symmetry [pcazade]
119 : ! **************************************************************************************************
120 905 : RECURSIVE SUBROUTINE geoopt_bfgs(force_env, gopt_param, globenv, geo_section, gopt_env, x0)
121 :
122 : TYPE(force_env_type), POINTER :: force_env
123 : TYPE(gopt_param_type), POINTER :: gopt_param
124 : TYPE(global_environment_type), POINTER :: globenv
125 : TYPE(section_vals_type), POINTER :: geo_section
126 : TYPE(gopt_f_type), POINTER :: gopt_env
127 : REAL(KIND=dp), DIMENSION(:), POINTER :: x0
128 :
129 : CHARACTER(len=*), PARAMETER :: routineN = 'geoopt_bfgs'
130 : REAL(KIND=dp), PARAMETER :: one = 1.0_dp, zero = 0.0_dp
131 :
132 : CHARACTER(LEN=5) :: wildcard
133 : INTEGER :: handle, info, iter_nr, its, &
134 : matrix_method, maxiter, ndf, nfree, &
135 : output_unit
136 : LOGICAL :: conv, hesrest, hess_reset, inverse, &
137 : shell_present, should_stop, &
138 : use_mod_hes, use_rfo
139 : REAL(KIND=dp) :: ediff, emin, eold, etot, pred, rad, rat, &
140 : step, t_diff, t_now, t_old
141 905 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: dg, dr, dx, eigval, gold, work, xold
142 905 : REAL(KIND=dp), DIMENSION(:), POINTER :: g
143 : TYPE(atomic_kind_list_type), POINTER :: atomic_kinds
144 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
145 : TYPE(cp_fm_struct_type), POINTER :: fm_struct_hes
146 : TYPE(cp_fm_type) :: eigvec_mat, hess_mat, hess_tmp
147 : TYPE(cp_logger_type), POINTER :: logger
148 : TYPE(cp_subsys_type), POINTER :: subsys
149 : TYPE(mp_para_env_type), POINTER :: para_env
150 : TYPE(section_vals_type), POINTER :: print_key, root_section
151 : TYPE(spgr_type), POINTER :: spgr
152 :
153 905 : NULLIFY (logger, g, blacs_env, spgr)
154 1810 : logger => cp_get_default_logger()
155 905 : para_env => force_env%para_env
156 905 : root_section => force_env%root_section
157 905 : spgr => gopt_env%spgr
158 905 : t_old = m_walltime()
159 :
160 905 : CALL timeset(routineN, handle)
161 905 : CALL section_vals_val_get(geo_section, "BFGS%TRUST_RADIUS", r_val=rad)
162 905 : print_key => section_vals_get_subs_vals(geo_section, "BFGS%RESTART")
163 905 : CALL section_vals_val_get(geo_section, "BFGS%METHOD", i_val=matrix_method)
164 905 : inverse = matrix_method == bfgs_inverse
165 905 : maxiter = gopt_param%max_iter
166 905 : conv = .FALSE.
167 905 : rat = 0.0_dp
168 905 : wildcard = " BFGS"
169 :
170 : ! Stop if not yet implemented
171 905 : SELECT CASE (gopt_env%type_id)
172 : CASE (default_ts_method_id)
173 905 : CPABORT("BFGS method not yet working with DIMER")
174 : END SELECT
175 :
176 905 : CALL section_vals_val_get(geo_section, "BFGS%USE_RAT_FUN_OPT", l_val=use_rfo)
177 905 : CALL section_vals_val_get(geo_section, "BFGS%USE_MODEL_HESSIAN", l_val=use_mod_hes)
178 905 : CALL section_vals_val_get(geo_section, "BFGS%RESTART_HESSIAN", l_val=hesrest)
179 905 : IF (inverse .AND. use_rfo) THEN
180 : CALL cp_abort(__LOCATION__, &
181 0 : "BFGS METHOD INVERSE does not support USE_RAT_FUN_OPT")
182 : END IF
183 : IF (inverse) THEN
184 10 : IF (.NOT. ieee_is_finite(rad) .OR. rad <= 0.0_dp) THEN
185 : CALL cp_abort(__LOCATION__, &
186 0 : "BFGS TRUST_RADIUS must be finite and positive")
187 : END IF
188 : END IF
189 : output_unit = cp_print_key_unit_nr(logger, geo_section, "PRINT%PROGRAM_RUN_INFO", &
190 905 : extension=".geoLog")
191 905 : IF (output_unit > 0) THEN
192 476 : IF (inverse) THEN
193 5 : WRITE (UNIT=output_unit, FMT="(/,T2,A)") "BFGS| Matrix update method: INVERSE"
194 : ELSE
195 471 : WRITE (UNIT=output_unit, FMT="(/,T2,A)") "BFGS| Matrix update method: DIRECT"
196 : END IF
197 476 : IF (use_rfo) THEN
198 : WRITE (UNIT=output_unit, FMT="(/,T2,A,T78,A3)") &
199 5 : "BFGS| Use rational function optimization for step estimation: ", "YES"
200 : ELSE
201 : WRITE (UNIT=output_unit, FMT="(/,T2,A,T78,A3)") &
202 471 : "BFGS| Use rational function optimization for step estimation: ", " NO"
203 : END IF
204 476 : IF (use_mod_hes) THEN
205 : WRITE (UNIT=output_unit, FMT="(T2,A,T78,A3)") &
206 412 : "BFGS| Use model Hessian for initial guess: ", "YES"
207 : ELSE
208 : WRITE (UNIT=output_unit, FMT="(T2,A,T78,A3)") &
209 64 : "BFGS| Use model Hessian for initial guess: ", " NO"
210 : END IF
211 476 : IF (hesrest) THEN
212 : WRITE (UNIT=output_unit, FMT="(T2,A,T78,A3)") &
213 4 : "BFGS| Restart Hessian: ", "YES"
214 : ELSE
215 : WRITE (UNIT=output_unit, FMT="(T2,A,T78,A3)") &
216 472 : "BFGS| Restart Hessian: ", " NO"
217 : END IF
218 : WRITE (UNIT=output_unit, FMT="(T2,A,T61,F20.3)") &
219 476 : "BFGS| Trust radius: ", rad
220 : END IF
221 :
222 905 : ndf = SIZE(x0)
223 905 : nfree = gopt_env%nfree
224 905 : IF (ndf > 3000 .AND. .NOT. inverse) THEN
225 : CALL cp_warn(__LOCATION__, &
226 : "The dimension of the Hessian matrix ("// &
227 : TRIM(ADJUSTL(cp_to_string(ndf)))//") is greater than 3000. "// &
228 : "The diagonalisation of the full Hessian matrix needed for BFGS "// &
229 : "is computationally expensive. You should consider to use the linear "// &
230 0 : "scaling variant L-BFGS instead.")
231 : END IF
232 :
233 : ! Initialize hessian (hes = unitary matrix or model hessian )
234 : CALL cp_blacs_env_create(blacs_env, para_env, globenv%blacs_grid_layout, &
235 905 : globenv%blacs_repeatable)
236 : CALL cp_fm_struct_create(fm_struct_hes, para_env=para_env, context=blacs_env, &
237 905 : nrow_global=ndf, ncol_global=ndf)
238 905 : CALL cp_fm_create(hess_mat, fm_struct_hes, name="hess_mat")
239 :
240 905 : CALL force_env_get(force_env=force_env, subsys=subsys)
241 905 : CALL cp_subsys_get(subsys, atomic_kinds=atomic_kinds)
242 905 : CALL get_atomic_kind_set(atomic_kind_set=atomic_kinds%els, shell_present=shell_present)
243 905 : IF (use_mod_hes) THEN
244 777 : IF (shell_present) THEN
245 : CALL cp_warn(__LOCATION__, &
246 : "No model Hessian is available for core-shell models. "// &
247 4 : "A unit matrix is used as the initial Hessian.")
248 4 : use_mod_hes = .FALSE.
249 : END IF
250 777 : IF (gopt_env%type_id == default_cell_method_id) THEN
251 : CALL cp_warn(__LOCATION__, &
252 : "No model Hessian is available for cell optimizations. "// &
253 0 : "A unit matrix is used as the initial Hessian.")
254 0 : use_mod_hes = .FALSE.
255 : END IF
256 : END IF
257 :
258 : ! hess_mat stores B for DIRECT and H = B^{-1} for INVERSE.
259 : ! DIRECT keeps the spectral work arrays for step construction; INVERSE
260 : ! only needs them while building a model-Hessian initial guess.
261 905 : IF (.NOT. inverse .OR. (use_mod_hes .AND. .NOT. hesrest)) THEN
262 901 : CALL cp_fm_create(hess_tmp, fm_struct_hes, name="hess_tmp")
263 901 : CALL cp_fm_create(eigvec_mat, fm_struct_hes, name="eigvec_mat")
264 2703 : ALLOCATE (eigval(ndf))
265 1802 : eigval(:) = zero
266 : END IF
267 :
268 905 : IF (inverse) THEN
269 10 : IF (use_mod_hes .AND. .NOT. hesrest) THEN
270 : CALL build_initial_hess(hess_mat, hess_tmp, eigvec_mat, eigval, force_env, &
271 6 : use_mod_hes, .TRUE.)
272 : ELSE
273 4 : CALL cp_fm_set_all(hess_mat, alpha=zero, beta=one)
274 : END IF
275 : ELSE
276 : CALL build_initial_hess(hess_mat, hess_tmp, eigvec_mat, eigval, force_env, &
277 895 : use_mod_hes, .FALSE.)
278 : END IF
279 :
280 905 : IF (inverse .AND. ALLOCATED(eigval)) THEN
281 6 : CALL cp_fm_release(eigvec_mat)
282 6 : CALL cp_fm_release(hess_tmp)
283 6 : DEALLOCATE (eigval)
284 : END IF
285 :
286 : ! Restore the reference coordinates before symmetry setup and the first evaluation.
287 905 : IF (hesrest) CALL read_bfgs_hessian(geo_section, hess_mat, logger, gopt_env, x0, matrix_method)
288 :
289 2715 : ALLOCATE (xold(ndf))
290 24785 : xold(:) = x0(:)
291 :
292 1810 : ALLOCATE (g(ndf))
293 24785 : g(:) = 0.0_dp
294 :
295 1810 : ALLOCATE (gold(ndf))
296 905 : gold(:) = 0.0_dp
297 :
298 1810 : ALLOCATE (dx(ndf))
299 905 : dx(:) = 0.0_dp
300 :
301 1810 : ALLOCATE (dg(ndf))
302 905 : dg(:) = 0.0_dp
303 :
304 1810 : ALLOCATE (work(ndf))
305 905 : work(:) = 0.0_dp
306 :
307 1810 : ALLOCATE (dr(ndf))
308 905 : dr(:) = 0.0_dp
309 :
310 : ! find space_group
311 905 : CALL section_vals_val_get(geo_section, "KEEP_SPACE_GROUP", l_val=spgr%keep_space_group)
312 905 : IF (spgr%keep_space_group) THEN
313 12 : CALL identify_space_group(subsys, geo_section, gopt_env, output_unit)
314 12 : CALL spgr_apply_rotations_coord(spgr, x0)
315 12 : CALL print_spgr(spgr)
316 : END IF
317 :
318 : ! Geometry optimization starts now
319 905 : CALL cp_iterate(logger%iter_info, increment=0, iter_nr_out=iter_nr)
320 905 : CALL print_geo_opt_header(gopt_env, output_unit, wildcard)
321 :
322 : ! Calculate Energy & Gradients
323 : CALL cp_eval_at(gopt_env, x0, etot, g, master=gopt_env%force_env%para_env%mepos, &
324 905 : para_env=gopt_env%force_env%para_env)
325 :
326 : ! Symmetrize coordinates and forces
327 905 : IF (spgr%keep_space_group) THEN
328 12 : CALL spgr_apply_rotations_coord(spgr, x0)
329 12 : CALL spgr_apply_rotations_force(spgr, g)
330 : END IF
331 :
332 : ! Print info at time 0
333 905 : emin = etot
334 905 : t_now = m_walltime()
335 905 : t_diff = t_now - t_old
336 905 : t_old = t_now
337 905 : CALL gopt_f_io_init(gopt_env, output_unit, etot, wildcard=wildcard, its=iter_nr, used_time=t_diff)
338 4352 : DO its = iter_nr + 1, maxiter
339 4342 : CALL cp_iterate(logger%iter_info, last=(its == maxiter))
340 4342 : CALL section_vals_val_set(geo_section, "STEP_START_VAL", i_val=its)
341 4342 : CALL gopt_f_ii(its, output_unit)
342 :
343 4342 : IF ((its - iter_nr) > 1) THEN
344 : ! Symmetrize old coordinates and old forces.
345 3447 : IF (spgr%keep_space_group) THEN
346 0 : CALL spgr_apply_rotations_coord(spgr, xold)
347 0 : CALL spgr_apply_rotations_force(spgr, gold)
348 : END IF
349 :
350 : ! cp_eval_at may project the proposed geometry: use the actual displacement.
351 324039 : dx(:) = x0(:) - xold(:)
352 324039 : dg(:) = g(:) - gold(:)
353 3447 : IF (inverse) THEN
354 502 : CALL bfgs_inverse_update(ndf, dx, dg, hess_mat, work)
355 : ELSE
356 2945 : CALL bfgs(ndf, dx, dg, hess_mat, work, para_env, output_unit, hess_reset)
357 2945 : IF (hess_reset) THEN
358 : CALL build_initial_hess(hess_mat, hess_tmp, eigvec_mat, eigval, force_env, &
359 0 : use_mod_hes, .FALSE.)
360 : END IF
361 : END IF
362 :
363 3447 : IF (spgr%keep_space_group) THEN
364 0 : CALL spgr_apply_rotations_force(spgr, dx)
365 0 : CALL spgr_apply_rotations_force(spgr, dg)
366 : END IF
367 :
368 3447 : IF (BTEST(cp_print_key_should_output(logger%iter_info, print_key), cp_p_file)) THEN
369 3124 : CALL write_bfgs_hessian(geo_section, hess_mat, logger, gopt_env, matrix_method)
370 : END IF
371 : END IF
372 :
373 : ! Symmetrize coordinates and forces
374 4342 : IF (spgr%keep_space_group) THEN
375 12 : CALL spgr_apply_rotations_coord(spgr, x0)
376 12 : CALL spgr_apply_rotations_force(spgr, g)
377 : END IF
378 :
379 : ! Setting the present positions & gradients as old
380 348670 : xold(:) = x0
381 348670 : gold(:) = g
382 :
383 4342 : IF (inverse) THEN
384 512 : CALL bfgs_inverse_step(hess_mat, g, dr)
385 : ELSE
386 : ! Copying hessian hes to (ndf x ndf) matrix hes_mat for diagonalization
387 3830 : CALL cp_fm_to_fm(hess_mat, hess_tmp)
388 :
389 3830 : CALL choose_eigv_solver(hess_tmp, eigvec_mat, eigval, info=info)
390 :
391 : ! In rare cases the diagonalization of hess_mat fails (bug in scalapack?)
392 3830 : IF (info /= 0) THEN
393 0 : IF (output_unit > 0) THEN
394 : WRITE (output_unit, *) &
395 0 : "BFGS: Matrix diagonalization failed, resetting Hessian to unity."
396 : END IF
397 0 : CALL cp_fm_set_all(hess_mat, alpha=zero, beta=one)
398 0 : CALL cp_fm_to_fm(hess_mat, hess_tmp)
399 0 : CALL choose_eigv_solver(hess_tmp, eigvec_mat, eigval)
400 : END IF
401 :
402 3830 : IF (use_rfo) THEN
403 506 : CALL set_hes_eig(ndf, eigval, work)
404 70286 : dx(:) = eigval
405 506 : CALL rat_fun_opt(ndf, dg, eigval, work, eigvec_mat, g, para_env)
406 : END IF
407 3830 : CALL geoopt_get_step(ndf, eigval, eigvec_mat, hess_tmp, dr, g, para_env, use_rfo)
408 :
409 : END IF
410 4342 : IF (gopt_env%type_id == default_cell_method_id) THEN
411 562 : CALL bfgs_project_cell_step(gopt_env, dr)
412 : END IF
413 :
414 : ! Symmetrize dr
415 4342 : IF (spgr%keep_space_group) THEN
416 12 : CALL spgr_apply_rotations_force(spgr, dr)
417 : END IF
418 :
419 4342 : IF (inverse) THEN
420 : ! Avoid a zero-step division and initialize the reported scale on every step.
421 512 : rat = 1.0_dp
422 79712 : step = MAXVAL(ABS(dr))
423 512 : IF (step > rad) THEN
424 16 : rat = rad/step
425 1864 : dr(:) = rat*dr(:)
426 : step = rad
427 : END IF
428 : ELSE
429 3830 : CALL trust_radius(ndf, step, rad, rat, dr, output_unit)
430 : END IF
431 :
432 : ! Update the atomic positions
433 348670 : x0 = x0 + dr
434 :
435 : ! Symmetrize coordinates
436 4342 : IF (spgr%keep_space_group) THEN
437 12 : CALL spgr_apply_rotations_coord(spgr, x0)
438 : END IF
439 :
440 4342 : eold = etot
441 :
442 : ! Energy & Gradients at new step
443 : CALL cp_eval_at(gopt_env, x0, etot, g, master=gopt_env%force_env%para_env%mepos, &
444 4342 : para_env=gopt_env%force_env%para_env)
445 :
446 : ! Use the displacement actually accepted by constraints and cell projection.
447 348670 : dr(:) = x0(:) - xold(:)
448 348670 : step = MAXVAL(ABS(dr))
449 :
450 : ! This diagnostic requires B, not H. It is optional in gopt_f_io.
451 4342 : IF (.NOT. inverse) CALL energy_predict(ndf, work, hess_mat, dr, gold, conv, pred, para_env)
452 :
453 4342 : ediff = etot - eold
454 :
455 : ! Symmetrize forces
456 4342 : IF (spgr%keep_space_group) THEN
457 12 : CALL spgr_apply_rotations_force(spgr, g)
458 : END IF
459 :
460 : ! check for an external exit command
461 4342 : CALL external_control(should_stop, "GEO", globenv=globenv)
462 4342 : IF (should_stop) EXIT
463 :
464 : ! Some IO and Convergence check
465 4342 : t_now = m_walltime()
466 4342 : t_diff = t_now - t_old
467 4342 : t_old = t_now
468 4342 : IF (inverse) THEN
469 : CALL gopt_f_io(gopt_env, force_env, root_section, its, etot, output_unit, &
470 : eold, emin, wildcard, gopt_param, ndf, dr, g, conv, &
471 512 : rat=rat, step=step, rad=rad, used_time=t_diff)
472 : ELSE
473 : CALL gopt_f_io(gopt_env, force_env, root_section, its, etot, output_unit, &
474 : eold, emin, wildcard, gopt_param, ndf, dr, g, conv, pred, rat, &
475 3830 : step, rad, used_time=t_diff)
476 : END IF
477 :
478 4342 : IF (conv .OR. (its == maxiter)) EXIT
479 3447 : IF (etot < emin) emin = etot
480 13036 : IF (use_rfo) CALL update_trust_rad(rat, rad, step, ediff)
481 : END DO
482 :
483 905 : IF (its == maxiter .AND. (.NOT. conv)) THEN
484 631 : CALL print_geo_opt_nc(gopt_env, output_unit)
485 : END IF
486 :
487 : ! show space_group
488 905 : CALL section_vals_val_get(geo_section, "SHOW_SPACE_GROUP", l_val=spgr%show_space_group)
489 905 : IF (spgr%show_space_group) THEN
490 2 : CALL identify_space_group(subsys, geo_section, gopt_env, output_unit)
491 2 : CALL print_spgr(spgr)
492 : END IF
493 :
494 : ! Write final information, if converged
495 905 : CALL cp_iterate(logger%iter_info, last=.TRUE., increment=0)
496 905 : CALL write_bfgs_hessian(geo_section, hess_mat, logger, gopt_env, matrix_method)
497 : CALL gopt_f_io_finalize(gopt_env, force_env, x0, conv, its, root_section, &
498 905 : gopt_env%force_env%para_env, gopt_env%force_env%para_env%mepos, output_unit)
499 :
500 905 : CALL cp_fm_struct_release(fm_struct_hes)
501 905 : CALL cp_fm_release(hess_mat)
502 905 : IF (.NOT. inverse) THEN
503 895 : CALL cp_fm_release(eigvec_mat)
504 895 : CALL cp_fm_release(hess_tmp)
505 : END IF
506 :
507 905 : CALL cp_blacs_env_release(blacs_env)
508 905 : DEALLOCATE (xold)
509 905 : DEALLOCATE (g)
510 905 : DEALLOCATE (gold)
511 905 : DEALLOCATE (dx)
512 905 : DEALLOCATE (dg)
513 905 : IF (ALLOCATED(eigval)) DEALLOCATE (eigval)
514 905 : DEALLOCATE (work)
515 905 : DEALLOCATE (dr)
516 :
517 : CALL cp_print_key_finished_output(output_unit, logger, geo_section, &
518 905 : "PRINT%PROGRAM_RUN_INFO")
519 905 : CALL timestop(handle)
520 :
521 7240 : END SUBROUTINE geoopt_bfgs
522 :
523 : ! **************************************************************************************************
524 : !> \brief Damped inverse-BFGS update, using only a matvec and local rank-two updates.
525 : !> \param ndf Number of optimization variables
526 : !> \param dx Actual change of optimization variables (not modified by damping)
527 : !> \param dg Change of gradients in the same coordinate system
528 : !> \param hess_mat Inverse Hessian, updated in place
529 : !> \param work Replicated workspace for H*dg
530 : ! **************************************************************************************************
531 502 : SUBROUTINE bfgs_inverse_update(ndf, dx, dg, hess_mat, work)
532 : INTEGER, INTENT(IN) :: ndf
533 : REAL(KIND=dp), INTENT(IN) :: dx(ndf), dg(ndf)
534 : TYPE(cp_fm_type), INTENT(IN) :: hess_mat
535 : REAL(KIND=dp), INTENT(OUT) :: work(ndf)
536 :
537 : REAL(KIND=dp), PARAMETER :: min_curvature = 0.2_dp
538 :
539 : INTEGER :: handle, i, ig, invalid, j, jg, nc, nr
540 502 : INTEGER, DIMENSION(:), POINTER :: cols, rows
541 : REAL(KIND=dp) :: coeff, rho, sy, theta, yhy
542 : REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
543 502 : POINTER :: data
544 502 : REAL(KIND=dp), DIMENSION(ndf) :: secant
545 :
546 502 : CALL timeset("bfgs_inverse_update", handle)
547 157856 : IF (.NOT. ALL(ieee_is_finite(dx)) .OR. .NOT. ALL(ieee_is_finite(dg))) THEN
548 0 : CPABORT("Non-finite displacement or gradient difference in inverse BFGS")
549 : END IF
550 78928 : work = 0.0_dp
551 157354 : IF (NORM2(dx) == 0.0_dp .OR. NORM2(dg) == 0.0_dp) THEN
552 0 : CALL timestop(handle)
553 0 : RETURN
554 : END IF
555 :
556 502 : CALL cp_fm_matvec(hess_mat, dg, work)
557 78928 : sy = DOT_PRODUCT(dx, dg)
558 78928 : yhy = DOT_PRODUCT(dg, work)
559 502 : IF (.NOT. ieee_is_finite(sy) .OR. .NOT. ieee_is_finite(yhy)) THEN
560 0 : CPABORT("Non-finite curvature in inverse BFGS")
561 : END IF
562 502 : IF (yhy <= 0.0_dp) THEN
563 : CALL cp_warn(__LOCATION__, &
564 0 : "BFGS: non-positive inverse curvature; resetting to unity.")
565 0 : CALL cp_fm_set_all(hess_mat, alpha=0.0_dp, beta=1.0_dp)
566 0 : CALL timestop(handle)
567 0 : RETURN
568 : END IF
569 :
570 78928 : secant = dx
571 502 : IF (sy < min_curvature*yhy) THEN
572 : ! Dual-space damping: y^T*s_tilde >= min_curvature*y^T*H*y.
573 : ! Unlike damping y with B*s, this does not need the direct Hessian.
574 4 : theta = ((1.0_dp - min_curvature)*yhy)/(yhy - sy)
575 712 : secant = theta*dx + (1.0_dp - theta)*work
576 712 : sy = DOT_PRODUCT(secant, dg)
577 : END IF
578 502 : IF (sy <= TINY(1.0_dp)/EPSILON(1.0_dp)) THEN
579 0 : CALL timestop(handle)
580 0 : RETURN
581 : END IF
582 502 : rho = 1.0_dp/sy
583 502 : coeff = (1.0_dp + yhy*rho)*rho
584 502 : IF (.NOT. ieee_is_finite(coeff)) THEN
585 : CALL cp_warn(__LOCATION__, &
586 0 : "BFGS: unrepresentable inverse update; retaining the previous matrix.")
587 0 : CALL timestop(handle)
588 0 : RETURN
589 : END IF
590 :
591 : CALL cp_fm_get_info(hess_mat, row_indices=rows, col_indices=cols, &
592 502 : local_data=DATA, nrow_local=nr, ncol_local=nc)
593 502 : !$OMP PARALLEL DO DEFAULT(NONE) SHARED(nc,nr,rows,cols,data,coeff,rho,secant,work) PRIVATE(i,j,ig,jg)
594 : DO j = 1, nc
595 : jg = cols(j)
596 : DO i = 1, nr
597 : ig = rows(i)
598 : DATA(i, j) = DATA(i, j) + coeff*(secant(ig)*secant(jg)) - &
599 : rho*(secant(ig)*work(jg) + work(ig)*secant(jg))
600 : END DO
601 : END DO
602 : !$OMP END PARALLEL DO
603 502 : invalid = 0
604 6569539 : IF (.NOT. ALL(ieee_is_finite(DATA(1:nr, 1:nc)))) invalid = 1
605 502 : CALL hess_mat%matrix_struct%para_env%max(invalid)
606 502 : IF (invalid /= 0) THEN
607 : CALL cp_warn(__LOCATION__, &
608 0 : "BFGS: non-finite inverse matrix; resetting to unity.")
609 0 : CALL cp_fm_set_all(hess_mat, alpha=0.0_dp, beta=1.0_dp)
610 : END IF
611 502 : CALL timestop(handle)
612 502 : END SUBROUTINE bfgs_inverse_update
613 :
614 : ! **************************************************************************************************
615 : !> \brief Form -H*g and recover a descent direction if roundoff has corrupted H.
616 : !> \param hess_mat Inverse Hessian
617 : !> \param g Gradient
618 : !> \param dr Search direction
619 : ! **************************************************************************************************
620 512 : SUBROUTINE bfgs_inverse_step(hess_mat, g, dr)
621 : TYPE(cp_fm_type), INTENT(IN) :: hess_mat
622 : REAL(KIND=dp), INTENT(IN) :: g(:)
623 : REAL(KIND=dp), INTENT(OUT) :: dr(:)
624 :
625 : REAL(KIND=dp) :: slope
626 :
627 79712 : IF (.NOT. ALL(ieee_is_finite(g))) THEN
628 0 : CALL cp_abort(__LOCATION__, "Non-finite gradient in inverse BFGS")
629 : END IF
630 79712 : dr = 0.0_dp
631 79712 : IF (MAXVAL(ABS(g)) == 0.0_dp) RETURN
632 512 : CALL cp_fm_matvec(hess_mat, g, dr, alpha=-1.0_dp)
633 79712 : slope = DOT_PRODUCT(g, dr)
634 512 : IF (.NOT. ieee_is_finite(slope) .OR. slope >= 0.0_dp) THEN
635 : CALL cp_warn(__LOCATION__, &
636 0 : "BFGS: inverse matrix is not a descent model; resetting to unity.")
637 0 : CALL cp_fm_set_all(hess_mat, alpha=0.0_dp, beta=1.0_dp)
638 512 : dr = -g
639 : END IF
640 : END SUBROUTINE bfgs_inverse_step
641 :
642 : ! **************************************************************************************************
643 : !> \brief Apply existing cell restrictions to a proposed BFGS direction.
644 : !> \param gopt_env Optimization environment
645 : !> \param dr Search direction
646 : ! **************************************************************************************************
647 562 : SUBROUTINE bfgs_project_cell_step(gopt_env, dr)
648 : TYPE(gopt_f_type), POINTER :: gopt_env
649 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT), TARGET :: dr
650 :
651 : REAL(KIND=dp), DIMENSION(:), POINTER :: cell_step
652 :
653 562 : cell_step => dr(SIZE(dr) - 5:SIZE(dr))
654 : CALL apply_cell_constraints(cell_step, gopt_env%cell_env%opt_cell, &
655 : gopt_env%cell_env%keep_angles, gopt_env%cell_env%keep_symmetry, &
656 562 : gopt_env%cell_env%constraint_id)
657 562 : END SUBROUTINE bfgs_project_cell_step
658 :
659 : ! **************************************************************************************************
660 : !> \brief Convert a restart matrix once, not during normal inverse-BFGS iterations.
661 : !> \param matrix Input matrix, replaced by its (regularized) inverse
662 : !> \param from_inverse Whether the input already represents the inverse Hessian
663 : ! **************************************************************************************************
664 6 : SUBROUTINE bfgs_invert_matrix(matrix, from_inverse)
665 : TYPE(cp_fm_type), INTENT(IN) :: matrix
666 : LOGICAL, INTENT(IN) :: from_inverse
667 :
668 : INTEGER :: handle, info, ndf
669 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigenvalues
670 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct
671 : TYPE(cp_fm_type) :: eigenvectors, scratch
672 :
673 6 : CALL timeset("bfgs_invert_matrix", handle)
674 6 : CALL cp_fm_get_info(matrix, matrix_struct=matrix_struct, nrow_global=ndf)
675 6 : CALL cp_fm_create(scratch, matrix_struct, name="bfgs_restart_scratch")
676 6 : CALL cp_fm_create(eigenvectors, matrix_struct, name="bfgs_restart_eigenvectors")
677 18 : ALLOCATE (eigenvalues(ndf))
678 6 : CALL cp_fm_to_fm(matrix, scratch)
679 6 : CALL choose_eigv_solver(scratch, eigenvectors, eigenvalues, info=info)
680 6 : IF (info /= 0) THEN
681 0 : CALL cp_abort(__LOCATION__, "BFGS restart matrix diagonalization failed")
682 : END IF
683 246 : IF (.NOT. ALL(ieee_is_finite(eigenvalues))) THEN
684 0 : CALL cp_abort(__LOCATION__, "Non-finite BFGS restart spectrum")
685 : END IF
686 6 : IF (from_inverse) THEN
687 : ! Do not apply the direct-Hessian cutoff to inverse-Hessian eigenvalues.
688 220 : IF (MINVAL(eigenvalues) <= 0.0_dp) THEN
689 : CALL cp_abort(__LOCATION__, &
690 0 : "BFGS inverse restart matrix is not positive definite")
691 : END IF
692 : ELSE
693 26 : IF (MINVAL(eigenvalues) < min_hessian_eigenvalue) THEN
694 : CALL cp_warn(__LOCATION__, &
695 0 : "BFGS: regularizing the direct restart before conversion to INVERSE.")
696 : END IF
697 26 : eigenvalues(:) = MAX(min_hessian_eigenvalue, eigenvalues(:))
698 : END IF
699 246 : eigenvalues(:) = 1.0_dp/eigenvalues(:)
700 246 : IF (.NOT. ALL(ieee_is_finite(eigenvalues))) THEN
701 : CALL cp_abort(__LOCATION__, &
702 0 : "Unrepresentable inverse of BFGS restart matrix")
703 : END IF
704 6 : CALL cp_fm_to_fm(eigenvectors, scratch)
705 6 : CALL cp_fm_column_scale(scratch, eigenvalues)
706 6 : CALL parallel_gemm("N", "T", ndf, ndf, ndf, 1.0_dp, scratch, eigenvectors, 0.0_dp, matrix)
707 6 : CALL cp_fm_release(scratch)
708 6 : CALL cp_fm_release(eigenvectors)
709 6 : DEALLOCATE (eigenvalues)
710 6 : CALL timestop(handle)
711 12 : END SUBROUTINE bfgs_invert_matrix
712 :
713 : ! **************************************************************************************************
714 : !> \brief Read the metadata of a tagged BFGS restart, or identify a legacy raw Hessian.
715 : !> \param unit File unit, returned positioned at the first matrix column
716 : !> \param stored_method Matrix representation in the file
717 : !> \param stored_ndf Matrix dimension stored in a tagged file; zero for legacy files
718 : !> \param stored_type Optimization type stored in a tagged file; zero for legacy files
719 : !> \param tagged Whether the file contains coordinate and representation metadata
720 : !> \param reference_cell Stored CELL_OPT reference cell
721 : !> \param cell_frame Stored canonical-to-input cell frame
722 : ! **************************************************************************************************
723 8 : SUBROUTINE bfgs_read_restart_header(unit, stored_method, stored_ndf, stored_type, tagged, &
724 : reference_cell, cell_frame)
725 : INTEGER, INTENT(IN) :: unit
726 : INTEGER, INTENT(OUT) :: stored_method, stored_ndf, stored_type
727 : LOGICAL, INTENT(OUT) :: tagged
728 : REAL(KIND=dp), DIMENSION(3, 3), INTENT(OUT) :: reference_cell, cell_frame
729 :
730 : CHARACTER(LEN=16) :: magic
731 : INTEGER :: ios, version
732 :
733 8 : stored_method = bfgs_direct
734 8 : stored_ndf = 0
735 8 : stored_type = 0
736 8 : reference_cell = 0.0_dp
737 8 : cell_frame = 0.0_dp
738 8 : magic = ""
739 8 : READ (unit, IOSTAT=ios) magic
740 8 : tagged = ios == 0 .AND. magic == bfgs_restart_magic
741 8 : REWIND (unit)
742 8 : IF (tagged) THEN
743 8 : READ (unit, IOSTAT=ios) magic, version, stored_method, stored_ndf, stored_type, &
744 16 : reference_cell, cell_frame
745 8 : IF (ios /= 0) CPABORT("Invalid BFGS restart header")
746 8 : IF (version /= bfgs_restart_version) THEN
747 0 : CALL cp_abort(__LOCATION__, "Unsupported BFGS restart version")
748 : END IF
749 8 : IF (stored_method /= bfgs_direct .AND. stored_method /= bfgs_inverse) THEN
750 0 : CPABORT("Unknown matrix representation in BFGS restart")
751 : END IF
752 8 : IF (stored_ndf <= 0) CPABORT("Invalid matrix dimension in BFGS restart")
753 208 : IF (.NOT. ALL(ieee_is_finite(reference_cell)) .OR. .NOT. ALL(ieee_is_finite(cell_frame))) THEN
754 0 : CPABORT("Non-finite coordinate frame in BFGS restart")
755 : END IF
756 : END IF
757 8 : END SUBROUTINE bfgs_read_restart_header
758 :
759 : ! **************************************************************************************************
760 : !> \brief Read and validate a tagged or legacy BFGS restart on the I/O rank.
761 : !> \param unit File unit, returned positioned at the first matrix column
762 : !> \param ndf Expected dimension
763 : !> \param opt_type Expected optimization type
764 : !> \param stored_method Matrix representation in the file
765 : !> \param tagged Whether the file contains coordinate and representation metadata
766 : !> \param reference_cell Stored CELL_OPT reference cell
767 : !> \param cell_frame Stored canonical-to-input cell frame
768 : ! **************************************************************************************************
769 4 : SUBROUTINE bfgs_read_header(unit, ndf, opt_type, stored_method, tagged, reference_cell, cell_frame)
770 : INTEGER, INTENT(IN) :: unit, ndf, opt_type
771 : INTEGER, INTENT(OUT) :: stored_method
772 : LOGICAL, INTENT(OUT) :: tagged
773 : REAL(KIND=dp), DIMENSION(3, 3), INTENT(OUT) :: reference_cell, cell_frame
774 :
775 : INTEGER :: ios, j, stored_ndf, stored_type
776 : REAL(KIND=dp) :: extra
777 8 : REAL(KIND=dp), DIMENSION(ndf) :: column
778 :
779 : CALL bfgs_read_restart_header(unit, stored_method, stored_ndf, stored_type, tagged, &
780 4 : reference_cell, cell_frame)
781 4 : IF (tagged) THEN
782 4 : IF (stored_ndf /= ndf) THEN
783 : CALL cp_abort(__LOCATION__, &
784 0 : "BFGS restart dimension does not match the optimization variables")
785 : END IF
786 4 : IF (stored_type /= opt_type) THEN
787 0 : CALL cp_abort(__LOCATION__, "BFGS restart optimization type does not match")
788 : END IF
789 : END IF
790 :
791 : ! Preflight before any collective read: reject truncated/non-finite payloads and
792 : ! incompatible legacy dimensions with a CP2K error, not a rank-local Fortran I/O failure.
793 136 : DO j = 1, ndf
794 132 : READ (unit, IOSTAT=ios) column
795 132 : IF (ios /= 0) THEN
796 : CALL cp_abort(__LOCATION__, &
797 0 : "Truncated BFGS restart or incompatible legacy matrix dimension")
798 : END IF
799 9784 : IF (.NOT. ALL(ieee_is_finite(column))) THEN
800 0 : CALL cp_abort(__LOCATION__, "Non-finite matrix in BFGS restart")
801 : END IF
802 : END DO
803 4 : READ (unit, IOSTAT=ios) extra
804 4 : IF (ios /= iostat_end) THEN
805 : CALL cp_abort(__LOCATION__, &
806 0 : "Extra matrix columns or invalid trailing data in BFGS restart")
807 : END IF
808 4 : REWIND (unit)
809 4 : IF (tagged) READ (unit)
810 4 : END SUBROUTINE bfgs_read_header
811 :
812 : ! **************************************************************************************************
813 : !> \brief Read a common BFGS restart, restoring its coordinates and converting if needed.
814 : !> \param geo_section BFGS input section
815 : !> \param hess_mat Matrix to initialize
816 : !> \param logger Logger
817 : !> \param gopt_env Optimization environment
818 : !> \param x0 Current coordinates, re-expressed in the stored reference cell for CELL_OPT
819 : !> \param matrix_method Requested in-memory representation
820 : ! **************************************************************************************************
821 8 : SUBROUTINE read_bfgs_hessian(geo_section, hess_mat, logger, gopt_env, x0, matrix_method)
822 : TYPE(section_vals_type), POINTER :: geo_section
823 : TYPE(cp_fm_type), INTENT(INOUT) :: hess_mat
824 : TYPE(cp_logger_type), POINTER :: logger
825 : TYPE(gopt_f_type), POINTER :: gopt_env
826 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: x0
827 : INTEGER, INTENT(IN) :: matrix_method
828 :
829 : CHARACTER(LEN=default_path_length) :: filename
830 : INTEGER :: ndf, stored_method, unit
831 : LOGICAL :: tagged
832 : REAL(KIND=dp), DIMENSION(3, 3) :: cell_frame, reference_cell
833 : TYPE(mp_para_env_type), POINTER :: para_env
834 :
835 8 : para_env => gopt_env%force_env%para_env
836 8 : ndf = SIZE(x0)
837 8 : unit = -1
838 8 : IF (para_env%is_source()) THEN
839 4 : CALL section_vals_val_get(geo_section, "BFGS%RESTART_FILE_NAME", c_val=filename)
840 4 : IF (LEN_TRIM(filename) == 0) filename = TRIM(logger%iter_info%project_name)//"-BFGS.Hessian"
841 : CALL open_file(file_name=TRIM(filename), file_status="OLD", &
842 4 : file_form="UNFORMATTED", file_action="READ", unit_number=unit)
843 4 : CALL bfgs_read_header(unit, ndf, gopt_env%type_id, stored_method, tagged, reference_cell, cell_frame)
844 : END IF
845 8 : CALL para_env%bcast(stored_method, 0)
846 8 : CALL para_env%bcast(tagged, 0)
847 8 : CALL para_env%bcast(reference_cell, 0)
848 8 : CALL para_env%bcast(cell_frame, 0)
849 8 : CALL cp_fm_read_unformatted(hess_mat, unit)
850 8 : IF (para_env%is_source()) CALL close_file(unit_number=unit)
851 :
852 8 : IF (gopt_env%type_id == default_cell_method_id) THEN
853 2 : IF (tagged) THEN
854 2 : CALL bfgs_restore_cell_frame(gopt_env, x0, reference_cell, cell_frame)
855 : ELSE
856 : CALL cp_warn(__LOCATION__, "Legacy BFGS restart has no reference cell; "// &
857 0 : "assuming the current CELL_OPT coordinate frame. Exact frame recovery is not possible.")
858 : END IF
859 : END IF
860 8 : IF (stored_method /= matrix_method) THEN
861 6 : CALL bfgs_invert_matrix(hess_mat, stored_method == bfgs_inverse)
862 : END IF
863 8 : END SUBROUTINE read_bfgs_hessian
864 :
865 : ! **************************************************************************************************
866 : !> \brief Re-express coordinates in the saved frame without changing physical positions.
867 : !> \param gopt_env Optimization environment
868 : !> \param x0 Optimization coordinates
869 : !> \param reference_cell Reference cell that defines the stored Hessian coordinates
870 : !> \param cell_frame Canonical-to-input cell orientation used when writing the matrix
871 : ! **************************************************************************************************
872 2 : SUBROUTINE bfgs_restore_cell_frame(gopt_env, x0, reference_cell, cell_frame)
873 : TYPE(gopt_f_type), POINTER :: gopt_env
874 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: x0
875 : REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN) :: reference_cell, cell_frame
876 :
877 : INTEGER :: i
878 : REAL(KIND=dp), DIMENSION(3, 3) :: transform
879 :
880 : ! A normal continuation preserves this orientation even when lengths and angles change.
881 : ! Do not silently attach a matrix to a separately rotated or reordered structure.
882 26 : IF (MAXVAL(ABS(cell_frame - gopt_env%cell_env%opt_to_input)) > 1.0E-8_dp) THEN
883 : CALL cp_abort(__LOCATION__, &
884 0 : "BFGS restart cell orientation differs; use a matching structure or RESTART_HESSIAN F")
885 : END IF
886 26 : IF (ABS(det_3x3(reference_cell)) <= EPSILON(1.0_dp)*MAXVAL(ABS(reference_cell))**3) THEN
887 0 : CPABORT("Singular reference cell in BFGS restart")
888 : END IF
889 134 : transform = MATMUL(reference_cell, inv_3x3(gopt_env%h_ref))
890 62 : DO i = 1, SIZE(x0) - 6, 3
891 1142 : x0(i:i + 2) = MATMUL(transform, x0(i:i + 2))
892 : END DO
893 26 : gopt_env%h_ref = reference_cell
894 2 : END SUBROUTINE bfgs_restore_cell_frame
895 :
896 : ! **************************************************************************************************
897 : !> \brief ...
898 : !> \param ndf ...
899 : !> \param dg ...
900 : !> \param eigval ...
901 : !> \param work ...
902 : !> \param eigvec_mat ...
903 : !> \param g ...
904 : !> \param para_env ...
905 : ! **************************************************************************************************
906 1012 : SUBROUTINE rat_fun_opt(ndf, dg, eigval, work, eigvec_mat, g, para_env)
907 :
908 : INTEGER, INTENT(IN) :: ndf
909 : REAL(KIND=dp), INTENT(INOUT) :: dg(ndf), eigval(ndf), work(ndf)
910 : TYPE(cp_fm_type), INTENT(IN) :: eigvec_mat
911 : REAL(KIND=dp), INTENT(INOUT) :: g(ndf)
912 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env
913 :
914 : CHARACTER(LEN=*), PARAMETER :: routineN = 'rat_fun_opt'
915 : REAL(KIND=dp), PARAMETER :: one = 1.0_dp
916 :
917 : INTEGER :: handle, i, indf, iref, iter, j, k, l, &
918 : maxit, ncol_local, nrow_local
919 506 : INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
920 : LOGICAL :: bisec, conv, fail, set
921 : REAL(KIND=dp) :: fun, fun1, fun2, fun3, fung, lam1, lam2, &
922 : ln, lp, ssize, step, stol
923 : REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
924 506 : POINTER :: local_data
925 :
926 506 : CALL timeset(routineN, handle)
927 :
928 506 : stol = 1.0E-8_dp
929 506 : ssize = 0.2_dp
930 506 : maxit = 999
931 506 : fail = .FALSE.
932 506 : bisec = .FALSE.
933 :
934 70286 : dg = 0._dp
935 :
936 : CALL cp_fm_get_info(eigvec_mat, row_indices=row_indices, col_indices=col_indices, &
937 506 : local_data=local_data, nrow_local=nrow_local, ncol_local=ncol_local)
938 :
939 40436 : DO i = 1, nrow_local
940 39930 : j = row_indices(i)
941 10895336 : DO k = 1, ncol_local
942 10854900 : l = col_indices(k)
943 10894830 : dg(l) = dg(l) + local_data(i, k)*g(j)
944 : END DO
945 : END DO
946 506 : CALL para_env%sum(dg)
947 :
948 506 : set = .FALSE.
949 :
950 : DO
951 :
952 : ! calculating Lambda
953 :
954 508 : lp = 0.0_dp
955 508 : iref = 1
956 508 : ln = 0.0_dp
957 508 : IF (eigval(iref) < 0.0_dp) ln = eigval(iref) - 0.01_dp
958 :
959 508 : conv = .FALSE.
960 508 : iter = 0
961 : DO
962 1133 : iter = iter + 1
963 1133 : fun = 0.0_dp
964 1133 : fung = 0.0_dp
965 150683 : DO indf = 1, ndf
966 149550 : fun = fun + dg(indf)**2/(ln - eigval(indf))
967 150683 : fung = fung - dg(indf)**2/((ln - eigval(indf))**2)
968 : END DO
969 1133 : fun = fun - ln
970 1133 : fung = fung - one
971 1133 : step = fun/fung
972 1133 : ln = ln - step
973 1133 : IF (ABS(step) < stol) THEN
974 : conv = .TRUE.
975 : EXIT
976 : END IF
977 625 : IF (iter >= maxit) EXIT
978 : END DO
979 : outer: DO
980 510 : IF (.NOT. conv) THEN
981 62 : conv = .FALSE.
982 62 : bisec = .TRUE.
983 62 : iter = 0
984 62 : maxit = 9999
985 62 : lam1 = 0.0_dp
986 62 : IF (eigval(iref) < 0.0_dp) lam1 = eigval(iref) - 0.01_dp
987 : fun1 = 0.0_dp
988 62 : DO indf = 1, ndf
989 62 : fun1 = fun1 + dg(indf)**2/(lam1 - eigval(indf))
990 : END DO
991 2 : fun1 = fun1 - lam1
992 2 : step = ABS(lam1)/1000.0_dp
993 : IF (step < ssize) step = ssize
994 : inner: DO
995 20000 : iter = iter + 1
996 20000 : IF (iter > maxit) THEN
997 : ln = 0.0_dp
998 508 : lp = 0.0_dp
999 : fail = .TRUE.
1000 : EXIT outer
1001 : END IF
1002 19998 : fun2 = 0.0_dp
1003 19998 : lam2 = lam1 - iter*step
1004 619938 : DO indf = 1, ndf
1005 619938 : fun2 = fun2 + dg(indf)**2/(lam2 - eigval(indf))
1006 : END DO
1007 19998 : fun2 = fun2 - lam2
1008 19998 : IF (fun2*fun1 < 0.0_dp) THEN
1009 : iter = 0
1010 : DO
1011 0 : iter = iter + 1
1012 0 : IF (iter > maxit) THEN
1013 : ln = 0.0_dp
1014 : lp = 0.0_dp
1015 : fail = .TRUE.
1016 : EXIT outer
1017 : END IF
1018 0 : step = (lam1 + lam2)/2
1019 0 : fun3 = 0.0_dp
1020 0 : DO indf = 1, ndf
1021 0 : fun3 = fun3 + dg(indf)**2/(step - eigval(indf))
1022 : END DO
1023 0 : fun3 = fun3 - step
1024 :
1025 0 : IF (ABS(step - lam2) < stol) THEN
1026 : ln = step
1027 : EXIT inner
1028 : END IF
1029 :
1030 0 : IF (fun3*fun1 < stol) THEN
1031 : lam2 = step
1032 : ELSE
1033 0 : lam1 = step
1034 : END IF
1035 : END DO
1036 : END IF
1037 : END DO inner
1038 : END IF
1039 508 : IF ((ln > eigval(iref)) .OR. ((ln > 0.0_dp) .AND. &
1040 : (eigval(iref) > 0.0_dp))) THEN
1041 :
1042 2 : IF (.NOT. bisec) THEN
1043 : conv = .FALSE.
1044 : CYCLE outer
1045 : END IF
1046 : ln = 0.0_dp
1047 : lp = 0.0_dp
1048 : fail = .TRUE.
1049 : END IF
1050 : EXIT outer
1051 : END DO outer
1052 :
1053 508 : IF (fail .AND. .NOT. set) THEN
1054 62 : set = .TRUE.
1055 62 : DO indf = 1, ndf
1056 62 : eigval(indf) = eigval(indf)*work(indf)
1057 : END DO
1058 : CYCLE
1059 : END IF
1060 :
1061 506 : IF (.NOT. set) THEN
1062 70224 : work(1:ndf) = one
1063 : END IF
1064 :
1065 70286 : DO indf = 1, ndf
1066 70286 : eigval(indf) = eigval(indf) - ln
1067 : END DO
1068 : EXIT
1069 : END DO
1070 :
1071 506 : CALL timestop(handle)
1072 :
1073 506 : END SUBROUTINE rat_fun_opt
1074 :
1075 : ! **************************************************************************************************
1076 : !> \brief Update the direct Hessian with the damped BFGS formula
1077 : !> \param ndf Number of optimization variables
1078 : !> \param dx Change of the optimization variables
1079 : !> \param dg Change of the gradients in the same coordinate system
1080 : !> \param hess_mat Direct Hessian, updated in place
1081 : !> \param work Replicated workspace, holds B*dx on exit
1082 : !> \param para_env ...
1083 : !> \param output_unit Geometry log unit, activates at the default MEDIUM print level
1084 : !> \param hess_reset Set if the update had to be refused and the caller must
1085 : !> collectively rebuild the initial Hessian
1086 : ! **************************************************************************************************
1087 5890 : SUBROUTINE bfgs(ndf, dx, dg, hess_mat, work, para_env, output_unit, hess_reset)
1088 : INTEGER, INTENT(IN) :: ndf
1089 : REAL(KIND=dp), INTENT(INOUT) :: dx(ndf), dg(ndf)
1090 : TYPE(cp_fm_type), INTENT(IN) :: hess_mat
1091 : REAL(KIND=dp), INTENT(INOUT) :: work(ndf)
1092 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env
1093 : INTEGER, INTENT(IN) :: output_unit
1094 : LOGICAL, INTENT(OUT) :: hess_reset
1095 :
1096 : CHARACTER(LEN=*), PARAMETER :: routineN = 'bfgs'
1097 : INTEGER, PARAMETER :: exit_reset = 2, exit_skip = 1, &
1098 : exit_update = 0
1099 : REAL(KIND=dp), PARAMETER :: min_curvature = 0.2_dp, one = 1.0_dp, &
1100 : min_sbs = 10.0_dp*TINY(one), &
1101 : zero = 0.0_dp
1102 :
1103 : INTEGER :: exit_flag, handle, i, invalid, j, k, l, &
1104 : ncol_local, nrow_local
1105 2945 : INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
1106 : REAL(KIND=dp) :: dxw, eta_i, eta_j, gdx, sbs, seta, sy, &
1107 : theta
1108 : REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
1109 2945 : POINTER :: local_hes
1110 :
1111 2945 : CALL timeset(routineN, handle)
1112 2945 : hess_reset = .FALSE.
1113 :
1114 : CALL cp_fm_get_info(hess_mat, row_indices=row_indices, col_indices=col_indices, &
1115 2945 : local_data=local_hes, nrow_local=nrow_local, ncol_local=ncol_local)
1116 :
1117 : ! Refuse corrupted input instead of poisoning the Hessian with Inf/NaN.
1118 490222 : IF (.NOT. ALL(ieee_is_finite(dx(1:ndf))) .OR. .NOT. ALL(ieee_is_finite(dg(1:ndf)))) THEN
1119 0 : CPABORT("Non-finite displacement or gradient difference")
1120 : END IF
1121 :
1122 245111 : work = zero
1123 144071 : DO i = 1, nrow_local
1124 141126 : j = row_indices(i)
1125 23076071 : DO k = 1, ncol_local
1126 22932000 : l = col_indices(k)
1127 23073126 : work(j) = work(j) + local_hes(i, k)*dx(l)
1128 : END DO
1129 : END DO
1130 :
1131 2945 : IF (PRESENT(para_env)) CALL para_env%sum(work)
1132 :
1133 245111 : sy = DOT_PRODUCT(dg(1:ndf), dx(1:ndf))
1134 245111 : sbs = DOT_PRODUCT(dx(1:ndf), work(1:ndf))
1135 :
1136 : ! One collective decision covers every early exit, so that all ranks
1137 : ! take the same path. sbs and sy share the units of the energy, so the
1138 : ! curvature floor is relative to sy. min_sbs only fences off values for
1139 : ! which the divisions in the update below would overflow: with a
1140 : ! smaller sbs, 0.2*sbs (the least possible seta) falls below the
1141 : ! smallest normal number. The floor is never negative, so a non-positive
1142 : ! sbs (lost positive definiteness) is caught as well.
1143 2945 : exit_flag = exit_update
1144 490222 : IF (DOT_PRODUCT(dx(1:ndf), dx(1:ndf)) == zero .OR. &
1145 : DOT_PRODUCT(dg(1:ndf), dg(1:ndf)) == zero) THEN
1146 0 : exit_flag = exit_skip
1147 2945 : ELSE IF (.NOT. ieee_is_finite(sy) .OR. .NOT. ieee_is_finite(sbs) .OR. &
1148 : sbs <= MAX(EPSILON(one)*ABS(sy), min_sbs)) THEN
1149 0 : exit_flag = exit_reset
1150 : END IF
1151 : ! The max folds the votes into the most conservative action all ranks
1152 : ! can agree on. The exit codes are ordered by severity, so a skip vote
1153 : ! on one rank and a reset vote on another reset both. Every rank takes
1154 : ! the same branch, so the rebuild stays collective.
1155 2945 : IF (PRESENT(para_env)) CALL para_env%max(exit_flag)
1156 :
1157 2945 : IF (exit_flag == exit_skip) THEN
1158 : ! A zero displacement or gradient difference carries no curvature
1159 : ! information, so there is nothing to learn from this step.
1160 2945 : ELSE IF (exit_flag == exit_reset) THEN
1161 : ! The Hessian has lost positive definiteness along dx or the predicted
1162 : ! curvature is not representable, so the secant pair cannot be damped:
1163 : ! the caller restarts from the initial Hessian and skips this update.
1164 : CALL cp_warn(__LOCATION__, &
1165 0 : "BFGS: vanishing or non-positive predicted curvature, resetting the Hessian")
1166 0 : hess_reset = .TRUE.
1167 : ELSE
1168 : ! Damped update after M.J.D. Powell, Lecture Notes in Mathematics 630 (1978),
1169 : ! eq. 3.7: whenever the measured curvature sy drops below 20% of the predicted
1170 : ! curvature s.B.s, replace y by eta = theta*y + (1-theta)*B*s. The blended
1171 : ! curvature satisfies eta.s = 0.2*s.B.s exactly, so the update stays positive
1172 : ! definite in exact arithmetic, also for secant pairs that no line search
1173 : ! checked. Because sbs passed the floor above, seta >= 0.2*sbs stays a
1174 : ! normal number, so one/seta cannot overflow and no further floor on seta
1175 : ! is needed.
1176 2945 : theta = one
1177 2945 : IF (sy < min_curvature*sbs) THEN
1178 57 : theta = ((one - min_curvature)*sbs)/(sbs - sy)
1179 57 : IF (output_unit > 0) THEN
1180 : WRITE (UNIT=output_unit, FMT="(T2,A,F10.4,A,F3.1,A)") &
1181 22 : "BFGS| Damped update, curvature ratio y.s/(s.B.s): ", sy/sbs, &
1182 44 : " (threshold ", min_curvature, ")"
1183 22 : CALL m_flush(output_unit)
1184 : END IF
1185 : END IF
1186 2945 : seta = theta*sy + (one - theta)*sbs
1187 2945 : gdx = one/seta
1188 2945 : dxw = one/sbs
1189 :
1190 144071 : DO i = 1, nrow_local
1191 141126 : j = row_indices(i)
1192 141126 : eta_i = theta*dg(j) + (one - theta)*work(j)
1193 23076071 : DO k = 1, ncol_local
1194 22932000 : l = col_indices(k)
1195 22932000 : eta_j = theta*dg(l) + (one - theta)*work(l)
1196 : local_hes(i, k) = local_hes(i, k) + gdx*eta_i*eta_j - &
1197 23073126 : dxw*work(j)*work(l)
1198 : END DO
1199 : END DO
1200 :
1201 : ! Collective verdict: if any local block is corrupted, every rank resets.
1202 2945 : invalid = 0
1203 23177111 : IF (.NOT. ALL(ieee_is_finite(local_hes(1:nrow_local, 1:ncol_local)))) invalid = 1
1204 2945 : IF (PRESENT(para_env)) CALL para_env%max(invalid)
1205 2945 : IF (invalid /= 0) THEN
1206 0 : CALL cp_warn(__LOCATION__, "BFGS: non-finite Hessian after the update, resetting")
1207 0 : hess_reset = .TRUE.
1208 : END IF
1209 : END IF
1210 :
1211 2945 : CALL timestop(handle)
1212 :
1213 2945 : END SUBROUTINE bfgs
1214 :
1215 : ! **************************************************************************************************
1216 : !> \brief Build the initial direct or inverse Hessian: sanitized model Hessian or unity
1217 : !> \param hess_mat BFGS matrix (direct or inverse Hessian), replaced in place
1218 : !> \param hess_tmp Eigensolver scratch matrix, same structure as hess_mat
1219 : !> \param eigvec Eigensolver eigenvector matrix, same structure as hess_mat
1220 : !> \param eigval Eigensolver workspace of length ndf
1221 : !> \param force_env Environment providing the model Hessian at the current geometry
1222 : !> \param use_model Build the sanitized model Hessian; otherwise fall back to unity
1223 : !> \param inverse Store the inverse of the sanitized model Hessian
1224 : !> \note Must be called by all ranks together: construct_initial_hess reduces internally.
1225 : ! **************************************************************************************************
1226 901 : SUBROUTINE build_initial_hess(hess_mat, hess_tmp, eigvec, eigval, force_env, use_model, inverse)
1227 : TYPE(cp_fm_type), INTENT(IN) :: hess_mat, hess_tmp, eigvec
1228 : REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigval
1229 : TYPE(force_env_type), POINTER :: force_env
1230 : LOGICAL, INTENT(IN) :: use_model, inverse
1231 :
1232 : CHARACTER(LEN=*), PARAMETER :: routineN = 'build_initial_hess'
1233 : REAL(KIND=dp), PARAMETER :: min_eig = 0.1_dp, one = 1.0_dp, &
1234 : zero = 0.0_dp
1235 :
1236 : INTEGER :: handle, indf, info, ndf
1237 :
1238 901 : CALL timeset(routineN, handle)
1239 :
1240 901 : IF (use_model) THEN
1241 771 : CPASSERT(ASSOCIATED(force_env))
1242 771 : ndf = SIZE(eigval)
1243 : ! The raw model Hessian is not guaranteed positive definite, so
1244 : ! eigenfactorize, floor the eigenvalues, and rebuild.
1245 771 : CALL cp_fm_set_all(hess_mat, alpha=zero)
1246 771 : CALL construct_initial_hess(force_env, hess_mat)
1247 771 : CALL cp_fm_to_fm(hess_mat, hess_tmp)
1248 771 : CALL choose_eigv_solver(hess_tmp, eigvec, eigval, info=info)
1249 771 : IF (info /= 0) THEN
1250 : ! In rare cases the diagonalization fails (bug in scalapack?)
1251 0 : CALL cp_warn(__LOCATION__, "BFGS: model Hessian diagonalization failed, using unity")
1252 0 : CALL cp_fm_set_all(hess_mat, alpha=zero, beta=one)
1253 0 : eigval = one
1254 : ELSE
1255 16755 : DO indf = 1, ndf
1256 15984 : IF (eigval(indf) < min_eig) eigval(indf) = min_eig
1257 16755 : IF (inverse) eigval(indf) = one/eigval(indf)
1258 : END DO
1259 771 : CALL cp_fm_to_fm(eigvec, hess_tmp)
1260 771 : CALL cp_fm_column_scale(eigvec, eigval)
1261 771 : CALL parallel_gemm("N", "T", ndf, ndf, ndf, one, hess_tmp, eigvec, zero, hess_mat)
1262 : END IF
1263 : ELSE
1264 : ! No sanitized prior exists (user opt-out, cell optimization, or
1265 : ! core-shell model): fall back to unity. eigval mirrors the unity
1266 : ! spectrum, keeping the INTENT(OUT) argument defined on every path.
1267 130 : CALL cp_fm_set_all(hess_mat, alpha=zero, beta=one)
1268 7810 : eigval = one
1269 : END IF
1270 :
1271 901 : CALL timestop(handle)
1272 :
1273 901 : END SUBROUTINE build_initial_hess
1274 :
1275 : ! **************************************************************************************************
1276 : !> \brief ...
1277 : !> \param ndf ...
1278 : !> \param eigval ...
1279 : !> \param work ...
1280 : ! **************************************************************************************************
1281 506 : SUBROUTINE set_hes_eig(ndf, eigval, work)
1282 : INTEGER, INTENT(IN) :: ndf
1283 : REAL(KIND=dp), INTENT(INOUT) :: eigval(ndf), work(ndf)
1284 :
1285 : CHARACTER(LEN=*), PARAMETER :: routineN = 'set_hes_eig'
1286 : REAL(KIND=dp), PARAMETER :: max_neg = -0.5_dp, max_pos = 5.0_dp, &
1287 : min_eig = 0.005_dp, one = 1.0_dp
1288 :
1289 : INTEGER :: handle, indf
1290 : LOGICAL :: neg
1291 :
1292 506 : CALL timeset(routineN, handle)
1293 :
1294 70286 : DO indf = 1, ndf
1295 69780 : IF (eigval(indf) < 0.0_dp) neg = .TRUE.
1296 70286 : IF (eigval(indf) > 1000.0_dp) eigval(indf) = 1000.0_dp
1297 : END DO
1298 70286 : DO indf = 1, ndf
1299 70286 : IF (eigval(indf) < 0.0_dp) THEN
1300 2 : IF (eigval(indf) < max_neg) THEN
1301 0 : eigval(indf) = max_neg
1302 2 : ELSE IF (eigval(indf) > -min_eig) THEN
1303 2 : eigval(indf) = -min_eig
1304 : END IF
1305 69778 : ELSE IF (eigval(indf) < 1000.0_dp) THEN
1306 69754 : IF (eigval(indf) < min_eig) THEN
1307 188 : eigval(indf) = min_eig
1308 69566 : ELSE IF (eigval(indf) > max_pos) THEN
1309 5 : eigval(indf) = max_pos
1310 : END IF
1311 : END IF
1312 : END DO
1313 :
1314 70286 : DO indf = 1, ndf
1315 70286 : IF (eigval(indf) < 0.0_dp) THEN
1316 2 : work(indf) = -one
1317 : ELSE
1318 69778 : work(indf) = one
1319 : END IF
1320 : END DO
1321 :
1322 506 : CALL timestop(handle)
1323 :
1324 506 : END SUBROUTINE set_hes_eig
1325 :
1326 : ! **************************************************************************************************
1327 : !> \brief ...
1328 : !> \param ndf ...
1329 : !> \param eigval ...
1330 : !> \param eigvec_mat ...
1331 : !> \param hess_tmp ...
1332 : !> \param dr ...
1333 : !> \param g ...
1334 : !> \param para_env ...
1335 : !> \param use_rfo ...
1336 : ! **************************************************************************************************
1337 11490 : SUBROUTINE geoopt_get_step(ndf, eigval, eigvec_mat, hess_tmp, dr, g, para_env, use_rfo)
1338 :
1339 : INTEGER, INTENT(IN) :: ndf
1340 : REAL(KIND=dp), INTENT(INOUT) :: eigval(ndf)
1341 : TYPE(cp_fm_type), INTENT(IN) :: eigvec_mat, hess_tmp
1342 : REAL(KIND=dp), INTENT(INOUT) :: dr(ndf), g(ndf)
1343 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env
1344 : LOGICAL :: use_rfo
1345 :
1346 : REAL(KIND=dp), PARAMETER :: one = 1.0_dp, zero = 0.0_dp
1347 :
1348 : INTEGER :: i, indf, j, k, l, ncol_local, nrow_local
1349 3830 : INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
1350 : REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
1351 3830 : POINTER :: local_data
1352 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct
1353 : TYPE(cp_fm_type) :: tmp
1354 :
1355 3830 : CALL cp_fm_to_fm(eigvec_mat, hess_tmp)
1356 3830 : IF (use_rfo) THEN
1357 70286 : DO indf = 1, ndf
1358 70286 : eigval(indf) = one/eigval(indf)
1359 : END DO
1360 : ELSE
1361 198672 : DO indf = 1, ndf
1362 198672 : eigval(indf) = one/MAX(min_hessian_eigenvalue, eigval(indf))
1363 : END DO
1364 : END IF
1365 :
1366 3830 : CALL cp_fm_column_scale(hess_tmp, eigval)
1367 3830 : CALL cp_fm_get_info(eigvec_mat, matrix_struct=matrix_struct)
1368 3830 : CALL cp_fm_create(tmp, matrix_struct, name="tmp")
1369 3830 : CALL cp_fm_set_all(tmp, alpha=zero)
1370 :
1371 3830 : CALL parallel_gemm("N", "T", ndf, ndf, ndf, one, hess_tmp, eigvec_mat, zero, tmp)
1372 :
1373 3830 : CALL cp_fm_transpose(tmp, hess_tmp)
1374 3830 : CALL cp_fm_release(tmp)
1375 :
1376 : ! New step
1377 :
1378 : CALL cp_fm_get_info(hess_tmp, row_indices=row_indices, col_indices=col_indices, &
1379 3830 : local_data=local_data, nrow_local=nrow_local, ncol_local=ncol_local)
1380 :
1381 268958 : dr = 0.0_dp
1382 158162 : DO i = 1, nrow_local
1383 154332 : j = row_indices(i)
1384 24787148 : DO k = 1, ncol_local
1385 24628986 : l = col_indices(k)
1386 24783318 : dr(j) = dr(j) - local_data(i, k)*g(l)
1387 : END DO
1388 : END DO
1389 :
1390 3830 : CALL para_env%sum(dr)
1391 :
1392 3830 : END SUBROUTINE geoopt_get_step
1393 :
1394 : ! **************************************************************************************************
1395 : !> \brief ...
1396 : !> \param ndf ...
1397 : !> \param step ...
1398 : !> \param rad ...
1399 : !> \param rat ...
1400 : !> \param dr ...
1401 : !> \param output_unit ...
1402 : ! **************************************************************************************************
1403 3830 : SUBROUTINE trust_radius(ndf, step, rad, rat, dr, output_unit)
1404 : INTEGER, INTENT(IN) :: ndf
1405 : REAL(KIND=dp), INTENT(INOUT) :: step, rad, rat, dr(ndf)
1406 : INTEGER, INTENT(IN) :: output_unit
1407 :
1408 : CHARACTER(LEN=*), PARAMETER :: routineN = 'trust_radius'
1409 : REAL(KIND=dp), PARAMETER :: one = 1.0_dp
1410 :
1411 : INTEGER :: handle
1412 : REAL(KIND=dp) :: scal
1413 :
1414 3830 : CALL timeset(routineN, handle)
1415 :
1416 268958 : step = MAXVAL(ABS(dr))
1417 3830 : scal = MAX(one, rad/step)
1418 :
1419 3830 : IF (step > rad) THEN
1420 366 : rat = rad/step
1421 366 : CALL DSCAL(ndf, rat, dr, 1)
1422 366 : step = rad
1423 366 : IF (output_unit > 0) THEN
1424 : WRITE (unit=output_unit, FMT="(/,T2,A,F8.5)") &
1425 193 : " Step is scaled; Scaling factor = ", rat
1426 193 : CALL m_flush(output_unit)
1427 : END IF
1428 : END IF
1429 3830 : CALL timestop(handle)
1430 :
1431 3830 : END SUBROUTINE trust_radius
1432 :
1433 : ! **************************************************************************************************
1434 : !> \brief ...
1435 : !> \param ndf ...
1436 : !> \param work ...
1437 : !> \param hess_mat ...
1438 : !> \param dr ...
1439 : !> \param g ...
1440 : !> \param conv ...
1441 : !> \param pred ...
1442 : !> \param para_env ...
1443 : ! **************************************************************************************************
1444 7660 : SUBROUTINE energy_predict(ndf, work, hess_mat, dr, g, conv, pred, para_env)
1445 :
1446 : INTEGER, INTENT(IN) :: ndf
1447 : REAL(KIND=dp), INTENT(INOUT) :: work(ndf)
1448 : TYPE(cp_fm_type), INTENT(IN) :: hess_mat
1449 : REAL(KIND=dp), INTENT(INOUT) :: dr(ndf), g(ndf)
1450 : LOGICAL, INTENT(INOUT) :: conv
1451 : REAL(KIND=dp), INTENT(INOUT) :: pred
1452 : TYPE(mp_para_env_type), POINTER :: para_env
1453 :
1454 : CHARACTER(LEN=*), PARAMETER :: routineN = 'energy_predict'
1455 : REAL(KIND=dp), PARAMETER :: zero = 0.0_dp
1456 :
1457 : INTEGER :: handle, i, j, k, l, ncol_local, &
1458 : nrow_local
1459 3830 : INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
1460 : REAL(KIND=dp) :: ener1, ener2
1461 : REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
1462 3830 : POINTER :: local_data
1463 :
1464 3830 : CALL timeset(routineN, handle)
1465 :
1466 268958 : ener1 = DOT_PRODUCT(g(1:ndf), dr(1:ndf))
1467 :
1468 : CALL cp_fm_get_info(hess_mat, row_indices=row_indices, col_indices=col_indices, &
1469 3830 : local_data=local_data, nrow_local=nrow_local, ncol_local=ncol_local)
1470 :
1471 268958 : work = zero
1472 158162 : DO i = 1, nrow_local
1473 154332 : j = row_indices(i)
1474 24787148 : DO k = 1, ncol_local
1475 24628986 : l = col_indices(k)
1476 24783318 : work(j) = work(j) + local_data(i, k)*dr(l)
1477 : END DO
1478 : END DO
1479 :
1480 3830 : CALL para_env%sum(work)
1481 268958 : ener2 = DOT_PRODUCT(dr(1:ndf), work(1:ndf))
1482 3830 : pred = ener1 + 0.5_dp*ener2
1483 3830 : conv = .FALSE.
1484 3830 : CALL timestop(handle)
1485 :
1486 3830 : END SUBROUTINE energy_predict
1487 :
1488 : ! **************************************************************************************************
1489 : !> \brief ...
1490 : !> \param rat ...
1491 : !> \param rad ...
1492 : !> \param step ...
1493 : !> \param ediff ...
1494 : ! **************************************************************************************************
1495 483 : SUBROUTINE update_trust_rad(rat, rad, step, ediff)
1496 :
1497 : REAL(KIND=dp), INTENT(INOUT) :: rat, rad, step, ediff
1498 :
1499 : CHARACTER(LEN=*), PARAMETER :: routineN = 'update_trust_rad'
1500 : REAL(KIND=dp), PARAMETER :: max_trust = 1.0_dp, min_trust = 0.1_dp
1501 :
1502 : INTEGER :: handle
1503 :
1504 483 : CALL timeset(routineN, handle)
1505 :
1506 483 : IF (rat > 4.0_dp) THEN
1507 0 : IF (ediff < 0.0_dp) THEN
1508 0 : rad = step*0.5_dp
1509 : ELSE
1510 0 : rad = step*0.25_dp
1511 : END IF
1512 483 : ELSE IF (rat > 2.0_dp) THEN
1513 0 : IF (ediff < 0.0_dp) THEN
1514 0 : rad = step*0.75_dp
1515 : ELSE
1516 0 : rad = step*0.5_dp
1517 : END IF
1518 483 : ELSE IF (rat > 4.0_dp/3.0_dp) THEN
1519 0 : IF (ediff < 0.0_dp) THEN
1520 0 : rad = step
1521 : ELSE
1522 0 : rad = step*0.75_dp
1523 : END IF
1524 483 : ELSE IF (rat > 10.0_dp/9.0_dp) THEN
1525 0 : IF (ediff < 0.0_dp) THEN
1526 0 : rad = step*1.25_dp
1527 : ELSE
1528 0 : rad = step
1529 : END IF
1530 483 : ELSE IF (rat > 0.9_dp) THEN
1531 28 : IF (ediff < 0.0_dp) THEN
1532 28 : rad = step*1.5_dp
1533 : ELSE
1534 0 : rad = step*1.25_dp
1535 : END IF
1536 455 : ELSE IF (rat > 0.75_dp) THEN
1537 129 : IF (ediff < 0.0_dp) THEN
1538 124 : rad = step*1.25_dp
1539 : ELSE
1540 5 : rad = step
1541 : END IF
1542 326 : ELSE IF (rat > 0.5_dp) THEN
1543 30 : IF (ediff < 0.0_dp) THEN
1544 30 : rad = step
1545 : ELSE
1546 0 : rad = step*0.75_dp
1547 : END IF
1548 296 : ELSE IF (rat > 0.25_dp) THEN
1549 0 : IF (ediff < 0.0_dp) THEN
1550 0 : rad = step*0.75_dp
1551 : ELSE
1552 0 : rad = step*0.5_dp
1553 : END IF
1554 296 : ELSE IF (ediff < 0.0_dp) THEN
1555 294 : rad = step*0.5_dp
1556 : ELSE
1557 2 : rad = step*0.25_dp
1558 : END IF
1559 :
1560 483 : rad = MAX(rad, min_trust)
1561 483 : rad = MIN(rad, max_trust)
1562 483 : CALL timestop(handle)
1563 :
1564 483 : END SUBROUTINE update_trust_rad
1565 :
1566 : ! **************************************************************************************************
1567 :
1568 : ! **************************************************************************************************
1569 : !> \brief ...
1570 : !> \param geo_section ...
1571 : !> \param hess_mat ...
1572 : !> \param logger ...
1573 : !> \param gopt_env Optimization coordinate system
1574 : !> \param matrix_method Stored matrix representation
1575 : ! **************************************************************************************************
1576 8058 : SUBROUTINE write_bfgs_hessian(geo_section, hess_mat, logger, gopt_env, matrix_method)
1577 :
1578 : TYPE(section_vals_type), POINTER :: geo_section
1579 : TYPE(cp_fm_type), INTENT(IN) :: hess_mat
1580 : TYPE(cp_logger_type), POINTER :: logger
1581 : TYPE(gopt_f_type), POINTER :: gopt_env
1582 : INTEGER, INTENT(IN) :: matrix_method
1583 :
1584 : CHARACTER(LEN=*), PARAMETER :: routineN = 'write_bfgs_hessian'
1585 :
1586 : INTEGER :: handle, hesunit, ndf
1587 : REAL(KIND=dp), DIMENSION(3, 3) :: cell_frame
1588 :
1589 4029 : CALL timeset(routineN, handle)
1590 4029 : CALL cp_fm_get_info(hess_mat, nrow_global=ndf)
1591 4029 : cell_frame = 0.0_dp
1592 10893 : IF (gopt_env%type_id == default_cell_method_id) cell_frame = gopt_env%cell_env%opt_to_input
1593 : hesunit = cp_print_key_unit_nr(logger, geo_section, "BFGS%RESTART", &
1594 : extension=".Hessian", file_form="UNFORMATTED", file_action="WRITE", &
1595 4029 : file_position="REWIND")
1596 4029 : IF (hesunit > 0) THEN
1597 2336 : WRITE (hesunit) bfgs_restart_magic, bfgs_restart_version, matrix_method, ndf, &
1598 32704 : gopt_env%type_id, gopt_env%h_ref, cell_frame
1599 : END IF
1600 : ! Store the native representation. Conversions occur only when reading a different METHOD.
1601 4029 : CALL cp_fm_write_unformatted(hess_mat, hesunit)
1602 4029 : CALL cp_print_key_finished_output(hesunit, logger, geo_section, "BFGS%RESTART")
1603 4029 : CALL timestop(handle)
1604 :
1605 4029 : END SUBROUTINE write_bfgs_hessian
1606 :
1607 : ! **************************************************************************************************
1608 : !> \brief Constructs model Hessian as described in https://doi.org/10.1016/0009-2614(95)00646-L.
1609 : !> \param force_env ...
1610 : !> \param hess_mat ...
1611 : !> \author Florian Schiffmann
1612 : ! **************************************************************************************************
1613 771 : SUBROUTINE construct_initial_hess(force_env, hess_mat)
1614 :
1615 : TYPE(force_env_type), POINTER :: force_env
1616 : TYPE(cp_fm_type), INTENT(IN) :: hess_mat
1617 :
1618 : INTEGER :: i, iat_col, iat_row, iglobal, iind, j, &
1619 : jat_row, jglobal, jind, k, natom, &
1620 : ncol_local, nrow_local, z
1621 771 : INTEGER, ALLOCATABLE, DIMENSION(:) :: at_row
1622 771 : INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
1623 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: d_ij, rho_ij
1624 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: r_ij
1625 : REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
1626 771 : POINTER :: fixed, local_data
1627 : REAL(KIND=dp), DIMENSION(3, 3) :: alpha, r0
1628 : TYPE(cell_type), POINTER :: cell
1629 : TYPE(cp_subsys_type), POINTER :: subsys
1630 : TYPE(particle_list_type), POINTER :: particles
1631 :
1632 771 : CALL cite_reference(Lindh1995)
1633 :
1634 771 : CALL force_env_get(force_env=force_env, subsys=subsys, cell=cell)
1635 : CALL cp_subsys_get(subsys, &
1636 771 : particles=particles)
1637 :
1638 3084 : alpha(1, :) = [1._dp, 0.3949_dp, 0.3949_dp]
1639 3084 : alpha(2, :) = [0.3494_dp, 0.2800_dp, 0.2800_dp]
1640 3084 : alpha(3, :) = [0.3494_dp, 0.2800_dp, 0.1800_dp]
1641 :
1642 3084 : r0(1, :) = [1.35_dp, 2.10_dp, 2.53_dp]
1643 3084 : r0(2, :) = [2.10_dp, 2.87_dp, 3.40_dp]
1644 3084 : r0(3, :) = [2.53_dp, 3.40_dp, 3.40_dp]
1645 :
1646 : CALL cp_fm_get_info(hess_mat, row_indices=row_indices, col_indices=col_indices, &
1647 771 : local_data=local_data, nrow_local=nrow_local, ncol_local=ncol_local)
1648 771 : natom = particles%n_els
1649 2313 : ALLOCATE (at_row(natom))
1650 3084 : ALLOCATE (rho_ij(natom, natom))
1651 2313 : ALLOCATE (d_ij(natom, natom))
1652 3855 : ALLOCATE (r_ij(natom, natom, 3))
1653 2313 : ALLOCATE (fixed(3, natom))
1654 22083 : fixed = 1.0_dp
1655 771 : CALL fix_atom_control(force_env, fixed)
1656 3084 : DO i = 1, 3
1657 35052 : CALL hess_mat%matrix_struct%para_env%min(fixed(i, :))
1658 : END DO
1659 771 : rho_ij = 0
1660 : !XXXX insert proper rows !XXX
1661 6099 : at_row = 3
1662 6099 : DO i = 1, natom
1663 5328 : CALL get_atomic_kind(atomic_kind=particles%els(i)%atomic_kind, z=z)
1664 5328 : IF (z <= 10) at_row(i) = 2
1665 11427 : IF (z <= 2) at_row(i) = 1
1666 : END DO
1667 5328 : DO i = 2, natom
1668 4557 : iat_row = at_row(i)
1669 68107 : DO j = 1, i - 1
1670 62779 : jat_row = at_row(j)
1671 : !pbc for a distance vector
1672 251116 : r_ij(j, i, :) = pbc(particles%els(i)%r, particles%els(j)%r, cell)
1673 251116 : r_ij(i, j, :) = -r_ij(j, i, :)
1674 251116 : d_ij(j, i) = NORM2(r_ij(j, i, :))
1675 62779 : d_ij(i, j) = d_ij(j, i)
1676 62779 : rho_ij(j, i) = EXP(alpha(jat_row, iat_row)*(r0(jat_row, iat_row)**2 - d_ij(j, i)**2))
1677 67336 : rho_ij(i, j) = rho_ij(j, i)
1678 : END DO
1679 : END DO
1680 16755 : DO i = 1, ncol_local
1681 15984 : iglobal = col_indices(i)
1682 15984 : iind = MOD(iglobal - 1, 3) + 1
1683 15984 : iat_col = (iglobal + 2)/3
1684 15984 : IF (iat_col > natom) CYCLE
1685 689622 : DO j = 1, nrow_local
1686 672867 : jglobal = row_indices(j)
1687 672867 : jind = MOD(jglobal - 1, 3) + 1
1688 672867 : iat_row = (jglobal + 2)/3
1689 672867 : IF (iat_row > natom) CYCLE
1690 672867 : IF (iat_row /= iat_col) THEN
1691 643716 : IF (d_ij(iat_row, iat_col) < 6.0_dp) THEN
1692 : local_data(j, i) = local_data(j, i) + &
1693 213228 : angle_second_deriv(r_ij, d_ij, rho_ij, iind, jind, iat_col, iat_row, natom)
1694 : END IF
1695 : ELSE
1696 : local_data(j, i) = local_data(j, i) + &
1697 29151 : angle_second_deriv(r_ij, d_ij, rho_ij, iind, jind, iat_col, iat_row, natom)
1698 : END IF
1699 672867 : IF (iat_col /= iat_row) THEN
1700 643716 : IF (d_ij(iat_row, iat_col) < 6.0_dp) THEN
1701 : local_data(j, i) = local_data(j, i) - &
1702 : dist_second_deriv(r_ij(iat_col, iat_row, :), &
1703 852912 : iind, jind, d_ij(iat_row, iat_col), rho_ij(iat_row, iat_col))
1704 : END IF
1705 : ELSE
1706 702018 : DO k = 1, natom
1707 672867 : IF (k == iat_col) CYCLE
1708 672867 : IF (d_ij(iat_row, k) < 6.0_dp) THEN
1709 : local_data(j, i) = local_data(j, i) + &
1710 : dist_second_deriv(r_ij(iat_col, k, :), &
1711 852912 : iind, jind, d_ij(iat_row, k), rho_ij(iat_row, k))
1712 : END IF
1713 : END DO
1714 : END IF
1715 688851 : IF (fixed(jind, iat_row) < 0.5_dp .OR. fixed(iind, iat_col) < 0.5_dp) THEN
1716 10161 : local_data(j, i) = 0.0_dp
1717 10161 : IF (jind == iind .AND. iat_row == iat_col) local_data(j, i) = 1.0_dp
1718 : END IF
1719 : END DO
1720 : END DO
1721 771 : DEALLOCATE (fixed)
1722 771 : DEALLOCATE (rho_ij)
1723 771 : DEALLOCATE (d_ij)
1724 771 : DEALLOCATE (r_ij)
1725 771 : DEALLOCATE (at_row)
1726 :
1727 1542 : END SUBROUTINE construct_initial_hess
1728 :
1729 : ! **************************************************************************************************
1730 : !> \brief ...
1731 : !> \param r1 ...
1732 : !> \param i ...
1733 : !> \param j ...
1734 : !> \param d ...
1735 : !> \param rho ...
1736 : !> \return ...
1737 : ! **************************************************************************************************
1738 426456 : PURE FUNCTION dist_second_deriv(r1, i, j, d, rho) RESULT(deriv)
1739 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: r1
1740 : INTEGER, INTENT(IN) :: i, j
1741 : REAL(KIND=dp), INTENT(IN) :: d, rho
1742 : REAL(KIND=dp) :: deriv
1743 :
1744 426456 : deriv = 0.45_dp*rho*(r1(i)*r1(j))/d**2
1745 426456 : END FUNCTION dist_second_deriv
1746 :
1747 : ! **************************************************************************************************
1748 : !> \brief ...
1749 : !> \param r_ij ...
1750 : !> \param d_ij ...
1751 : !> \param rho_ij ...
1752 : !> \param idir ...
1753 : !> \param jdir ...
1754 : !> \param iat_der ...
1755 : !> \param jat_der ...
1756 : !> \param natom ...
1757 : !> \return ...
1758 : ! **************************************************************************************************
1759 242379 : FUNCTION angle_second_deriv(r_ij, d_ij, rho_ij, idir, jdir, iat_der, jat_der, natom) RESULT(deriv)
1760 : REAL(KIND=dp), DIMENSION(:, :, :) :: r_ij
1761 : REAL(KIND=dp), DIMENSION(:, :) :: d_ij, rho_ij
1762 : INTEGER :: idir, jdir, iat_der, jat_der, natom
1763 : REAL(KIND=dp) :: deriv
1764 :
1765 : REAL(KIND=dp), PARAMETER :: rho_thre = 1.0E-5_dp
1766 :
1767 : INTEGER :: i, iat, idr, j, jat, jdr
1768 : REAL(KIND=dp) :: d12, d23, d31, D_mat(3, 2), denom1, &
1769 : denom2, denom3, ka1, ka2, ka3, rho12, &
1770 : rho23, rho31, rsst1, rsst2, rsst3
1771 : REAL(KIND=dp), DIMENSION(3) :: r12, r23, r31
1772 :
1773 242379 : deriv = 0._dp
1774 242379 : IF (iat_der == jat_der) THEN
1775 672867 : DO i = 1, natom - 1
1776 643716 : IF (rho_ij(iat_der, i) < rho_thre) CYCLE
1777 3570147 : DO j = i + 1, natom
1778 3344508 : IF (rho_ij(iat_der, j) < rho_thre) CYCLE
1779 968355 : IF (i == iat_der .OR. j == iat_der) CYCLE
1780 968355 : IF (iat_der < i .OR. iat_der > j) THEN
1781 5870160 : r12 = r_ij(iat_der, i, :); r23 = r_ij(i, j, :); r31 = r_ij(j, iat_der, :)
1782 587016 : d12 = d_ij(iat_der, i); d23 = d_ij(i, j); d31 = d_ij(j, iat_der)
1783 587016 : rho12 = rho_ij(iat_der, i); rho23 = rho_ij(i, j); rho31 = rho_ij(j, iat_der)
1784 : ELSE
1785 3813390 : r12 = r_ij(iat_der, j, :); r23 = r_ij(j, i, :); r31 = r_ij(i, iat_der, :)
1786 381339 : d12 = d_ij(iat_der, j); d23 = d_ij(j, i); d31 = d_ij(i, iat_der)
1787 381339 : rho12 = rho_ij(iat_der, j); rho23 = rho_ij(j, i); rho31 = rho_ij(i, iat_der)
1788 : END IF
1789 968355 : ka1 = 0.15_dp*rho12*rho23; ka2 = 0.15_dp*rho23*rho31; ka3 = 0.15_dp*rho31*rho12
1790 9683550 : rsst1 = DOT_PRODUCT(r12, r23); rsst2 = DOT_PRODUCT(r23, r31); rsst3 = DOT_PRODUCT(r31, r12)
1791 968355 : denom1 = 1.0_dp - rsst1**2/(d12**2*d23**2); denom2 = 1.0_dp - rsst2**2/(d23**2*d31**2)
1792 968355 : denom3 = 1.0_dp - rsst3**2/(d31**2*d12**2)
1793 968355 : denom1 = SIGN(1.0_dp, denom1)*MAX(ABS(denom1), 0.01_dp)
1794 968355 : denom2 = SIGN(1.0_dp, denom2)*MAX(ABS(denom2), 0.01_dp)
1795 968355 : denom3 = SIGN(1.0_dp, denom3)*MAX(ABS(denom3), 0.01_dp)
1796 968355 : D_mat(1, 1) = r23(idir)/(d12*d23) - rsst1*r12(idir)/(d12**3*d23)
1797 968355 : D_mat(1, 2) = r23(jdir)/(d12*d23) - rsst1*r12(jdir)/(d12**3*d23)
1798 968355 : D_mat(2, 1) = -r23(idir)/(d23*d31) + rsst2*r31(idir)/(d23*d31**3)
1799 968355 : D_mat(2, 2) = -r23(jdir)/(d23*d31) + rsst2*r31(jdir)/(d23*d31**3)
1800 : D_mat(3, 1) = (r31(idir) - r12(idir))/(d31*d12) + rsst3*r31(idir)/(d31**3*d12) - &
1801 968355 : rsst3*r12(idir)/(d31*d12**3)
1802 : D_mat(3, 2) = (r31(jdir) - r12(jdir))/(d31*d12) + rsst3*r31(jdir)/(d31**3*d12) - &
1803 968355 : rsst3*r12(jdir)/(d31*d12**3)
1804 968355 : IF (ABS(denom1) <= 0.011_dp) D_mat(1, 1) = 0.0_dp
1805 968355 : IF (ABS(denom2) <= 0.011_dp) D_mat(2, 1) = 0.0_dp
1806 968355 : IF (ABS(denom3) <= 0.011_dp) D_mat(3, 1) = 0.0_dp
1807 : deriv = deriv + ka1*D_mat(1, 1)*D_mat(1, 2)/denom1 + &
1808 : ka2*D_mat(2, 1)*D_mat(2, 2)/denom2 + &
1809 3988224 : ka3*D_mat(3, 1)*D_mat(3, 2)/denom3
1810 :
1811 : END DO
1812 : END DO
1813 : ELSE
1814 6346098 : DO i = 1, natom
1815 6132870 : IF (i == iat_der .OR. i == jat_der) CYCLE
1816 5706414 : IF (jat_der < iat_der) THEN
1817 2853207 : iat = jat_der; jat = iat_der; idr = jdir; jdr = idir
1818 : ELSE
1819 2853207 : iat = iat_der; jat = jat_der; idr = idir; jdr = jdir
1820 : END IF
1821 5706414 : IF (jat < i .OR. iat > i) THEN
1822 45388620 : r12 = r_ij(iat, jat, :); r23 = r_ij(jat, i, :); r31 = r_ij(i, iat, :)
1823 4538862 : d12 = d_ij(iat, jat); d23 = d_ij(jat, i); d31 = d_ij(i, iat)
1824 4538862 : rho12 = rho_ij(iat, jat); rho23 = rho_ij(jat, i); rho31 = rho_ij(i, iat)
1825 : ELSE
1826 11675520 : r12 = r_ij(iat, i, :); r23 = r_ij(i, jat, :); r31 = r_ij(jat, iat, :)
1827 1167552 : d12 = d_ij(iat, i); d23 = d_ij(i, jat); d31 = d_ij(jat, iat)
1828 1167552 : rho12 = rho_ij(iat, i); rho23 = rho_ij(i, jat); rho31 = rho_ij(jat, iat)
1829 : END IF
1830 5706414 : ka1 = 0.15_dp*rho12*rho23; ka2 = 0.15_dp*rho23*rho31; ka3 = 0.15_dp*rho31*rho12
1831 57064140 : rsst1 = DOT_PRODUCT(r12, r23); rsst2 = DOT_PRODUCT(r23, r31); rsst3 = DOT_PRODUCT(r31, r12)
1832 5706414 : denom1 = 1.0_dp - rsst1**2/(d12**2*d23**2); denom2 = 1.0_dp - rsst2**2/(d23**2*d31**2)
1833 5706414 : denom3 = 1.0_dp - rsst3**2/(d31**2*d12**2)
1834 5706414 : denom1 = SIGN(1.0_dp, denom1)*MAX(ABS(denom1), 0.01_dp)
1835 5706414 : denom2 = SIGN(1.0_dp, denom2)*MAX(ABS(denom2), 0.01_dp)
1836 5706414 : denom3 = SIGN(1.0_dp, denom3)*MAX(ABS(denom3), 0.01_dp)
1837 5706414 : D_mat(1, 1) = r23(idr)/(d12*d23) - rsst1*r12(idr)/(d12**3*d23)
1838 5706414 : D_mat(2, 1) = -r23(idr)/(d23*d31) + rsst2*r31(idr)/(d23*d31**3)
1839 : D_mat(3, 1) = (r31(idr) - r12(idr))/(d31*d12) + rsst3*r31(idr)/(d31**3*d12) - &
1840 5706414 : rsst3*r12(idr)/(d31*d12**3)
1841 5706414 : IF (jat < i .OR. iat > i) THEN
1842 : D_mat(1, 2) = (r12(jdr) - r23(jdr))/(d12*d23) + rsst1*r12(jdr)/(d12**3*d23) - &
1843 4538862 : rsst1*r23(jdr)/(d12*d23**3)
1844 4538862 : D_mat(2, 2) = r31(jdr)/(d23*d31) - rsst2*r23(jdr)/(d23**3*d31)
1845 4538862 : D_mat(3, 2) = -r31(jdr)/(d31*d12) + rsst3*r12(jdr)/(d31*d12**3)
1846 : ELSE
1847 1167552 : D_mat(1, 2) = -r12(jdr)/(d12*d23) + rsst1*r23(jdr)/(d12*d23**3)
1848 : D_mat(2, 2) = (r23(jdr) - r31(jdr))/(d23*d31) + rsst2*r23(jdr)/(d23**3*d31) - &
1849 1167552 : rsst2*r31(jdr)/(d23*d31**3)
1850 1167552 : D_mat(3, 2) = r12(jdr)/(d31*d12) - rsst3*r31(jdr)/(d31**3*d12)
1851 : END IF
1852 5706414 : IF (ABS(denom1) <= 0.011_dp) D_mat(1, 1) = 0.0_dp
1853 5706414 : IF (ABS(denom2) <= 0.011_dp) D_mat(2, 1) = 0.0_dp
1854 5706414 : IF (ABS(denom3) <= 0.011_dp) D_mat(3, 1) = 0.0_dp
1855 :
1856 : deriv = deriv + ka1*D_mat(1, 1)*D_mat(1, 2)/denom1 + &
1857 : ka2*D_mat(2, 1)*D_mat(2, 2)/denom2 + &
1858 6346098 : ka3*D_mat(3, 1)*D_mat(3, 2)/denom3
1859 : END DO
1860 : END IF
1861 242379 : deriv = 0.25_dp*deriv
1862 :
1863 242379 : END FUNCTION angle_second_deriv
1864 :
1865 : END MODULE bfgs_optimizer
|