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 orbital transformations
10 : !> \par History
11 : !> None
12 : !> \author Joost VandeVondele (09.2002)
13 : ! **************************************************************************************************
14 : MODULE qs_ot_minimizer
15 :
16 : USE cp_dbcsr_api, ONLY: dbcsr_add,&
17 : dbcsr_copy,&
18 : dbcsr_get_info,&
19 : dbcsr_p_type,&
20 : dbcsr_release,&
21 : dbcsr_scale,&
22 : dbcsr_set,&
23 : dbcsr_type
24 : USE cp_dbcsr_contrib, ONLY: dbcsr_dot,&
25 : dbcsr_dot_batch,&
26 : dbcsr_init_random
27 : USE cp_dbcsr_operations, ONLY: dbcsr_sum_many
28 : USE cp_log_handling, ONLY: cp_get_default_logger,&
29 : cp_logger_get_default_unit_nr,&
30 : cp_logger_type
31 : USE cp_output_handling, ONLY: high_print_level
32 : USE ieee_arithmetic, ONLY: ieee_is_finite
33 : USE kinds, ONLY: dp,&
34 : int_8
35 : USE mathlib, ONLY: diamat_all
36 : USE message_passing, ONLY: mp_comm_type,&
37 : mp_para_env_type
38 : USE preconditioner, ONLY: apply_preconditioner,&
39 : apply_preconditioner_complex_batch
40 : USE preconditioner_types, ONLY: preconditioner_p_type
41 : USE qs_ot, ONLY: qs_ot_get_derivative,&
42 : qs_ot_get_derivative_ref
43 : USE qs_ot_kp, ONLY: qs_ot_get_derivative_complex,&
44 : qs_ot_get_derivative_local_batch,&
45 : qs_ot_get_derivative_ref_complex,&
46 : qs_ot_prepare_complex_tangent_metric,&
47 : qs_ot_prepare_complex_tangent_metric_batch,&
48 : qs_ot_project_gradient
49 : USE qs_ot_types, ONLY: qs_ot_kpoint_preconditioner_scale,&
50 : qs_ot_reset_history,&
51 : qs_ot_type
52 : #include "./base/base_uses.f90"
53 :
54 : IMPLICIT NONE
55 :
56 : PRIVATE
57 :
58 : PUBLIC :: broyden_history_restart_required, &
59 : cg_history_restart_required, &
60 : lbfgs_curvature_damping_shift, &
61 : lbfgs_history_restart_required, &
62 : lbfgs_response_secant_parameters, &
63 : lbfgs_step_restart_required, &
64 : ot_mermin_response_assess, &
65 : ot_mermin_response_candidate_preferred, &
66 : ot_mermin_response_compare, &
67 : ot_mermin_response_preparation_needed, &
68 : ot_mermin_response_probe, &
69 : ot_mermin_response_shadow_followup, &
70 : ot_mermin_secant_curvature, &
71 : ot_mini, &
72 : ot_mini_prepare, &
73 : ot_mini_prepare_gradient
74 :
75 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_ot_minimizer'
76 :
77 : CONTAINS
78 :
79 : ! **************************************************************************************************
80 : !> \brief Decide whether a non-descent Broyden step invalidates its secant history.
81 : !> \param non_descent whether the proposed Broyden direction is not a descent direction
82 : !> \param forget_history explicit user request to discard an inconsistent history
83 : !> \param do_ener whether the product vector contains Mermin auxiliary-energy variables
84 : !> \return true when the Broyden history has to be restarted
85 : ! **************************************************************************************************
86 12 : PURE ELEMENTAL LOGICAL FUNCTION broyden_history_restart_required( &
87 : non_descent, forget_history, do_ener)
88 : LOGICAL, INTENT(IN) :: non_descent, forget_history, do_ener
89 :
90 12 : broyden_history_restart_required = non_descent .AND. (forget_history .OR. do_ener)
91 12 : END FUNCTION broyden_history_restart_required
92 :
93 : ! **************************************************************************************************
94 : !> \brief Decide whether an unresolved accepted energy change invalidates CG conjugacy.
95 : !>
96 : !> The occupation-preconditioned Mermin direction is a coupled Schur step rather than the action of
97 : !> one fixed positive-definite metric on the physical gradient. Once the accepted free-energy change
98 : !> is below floating-point resolution, its nonlinear conjugacy cannot be calibrated reliably. The
99 : !> current preconditioned descent direction remains valid, but the history contribution is discarded.
100 : !> \param occupation_preconditioned whether the product direction uses occupation preconditioning
101 : !> \param current_energy current Mermin free energy
102 : !> \param reference_energy Mermin free energy before the accepted line search
103 : !> \return true when the CG history contribution has to be discarded
104 : ! **************************************************************************************************
105 23136 : PURE ELEMENTAL LOGICAL FUNCTION cg_history_restart_required( &
106 : occupation_preconditioned, current_energy, reference_energy)
107 : LOGICAL, INTENT(IN) :: occupation_preconditioned
108 : REAL(KIND=dp), INTENT(IN) :: current_energy, reference_energy
109 :
110 : REAL(KIND=dp) :: energy_scale
111 :
112 23136 : energy_scale = MAX(1.0_dp, ABS(current_energy), ABS(reference_energy))
113 : cg_history_restart_required = occupation_preconditioned .AND. &
114 : ABS(current_energy - reference_energy) <= &
115 23136 : 64.0_dp*EPSILON(1.0_dp)*energy_scale
116 :
117 23136 : END FUNCTION cg_history_restart_required
118 :
119 : !
120 : ! the minimizer interface
121 : ! should present all possible modes of minimization
122 : ! these include CG SD DIIS
123 : !
124 : !
125 : ! IN the case of nspin != 1 we have a gradient that is distributed over different qs_ot_env.
126 : ! still things remain basically the same, since there are no constraints between the different qs_ot_env
127 : ! we only should take care that the various scalar products are taken over the full vectors.
128 : ! all the information needed and collected can be stored in the fist qs_ot_env only
129 : ! (indicating that the data type for the gradient/position and minization should be separated)
130 : !
131 : ! **************************************************************************************************
132 : !> \brief ...
133 : !> \param qs_ot_env ...
134 : !> \param matrix_hc ...
135 : !> \param matrix_hc_im ...
136 : !> \param matrix_hc_physical occupation-weighted derivative used for slopes and convergence
137 : !> \param matrix_hc_physical_im imaginary component of matrix_hc_physical
138 : !> \param para_env_inter_kp communicator between distributed k-point groups
139 : !> \param gradient_only return after evaluating the current OT gradient
140 : !> \param gradient_prepared reuse a gradient evaluated by ot_mini_prepare_gradient
141 : !> \param restart_history pending restart from ot_mini_prepare; absent prepares here
142 : ! **************************************************************************************************
143 97516 : SUBROUTINE ot_mini(qs_ot_env, matrix_hc, matrix_hc_im, &
144 : matrix_hc_physical, matrix_hc_physical_im, para_env_inter_kp, &
145 : gradient_only, gradient_prepared, restart_history)
146 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
147 : TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: matrix_hc
148 : TYPE(dbcsr_p_type), DIMENSION(:), OPTIONAL, &
149 : POINTER :: matrix_hc_im, matrix_hc_physical, &
150 : matrix_hc_physical_im
151 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
152 : LOGICAL, INTENT(IN), OPTIONAL :: gradient_only, gradient_prepared, &
153 : restart_history
154 :
155 : CHARACTER(len=*), PARAMETER :: routineN = 'ot_mini'
156 :
157 : INTEGER :: handle, ispin, npasses, nspin, pass
158 : LOGICAL :: do_ener, do_ks, evaluate_gradient_only, &
159 : local_gradient, restart, reuse_gradient
160 : REAL(KIND=dp) :: tmp
161 : TYPE(dbcsr_type), POINTER :: hc_im, hc_re
162 :
163 97516 : CALL timeset(routineN, handle)
164 :
165 97516 : evaluate_gradient_only = .FALSE.
166 97516 : reuse_gradient = .FALSE.
167 97516 : IF (PRESENT(gradient_only)) evaluate_gradient_only = gradient_only
168 97516 : IF (PRESENT(gradient_prepared)) reuse_gradient = gradient_prepared
169 3050 : CPASSERT(.NOT. (evaluate_gradient_only .AND. reuse_gradient))
170 :
171 97516 : nspin = SIZE(qs_ot_env)
172 :
173 97516 : do_ks = qs_ot_env(1)%settings%ks
174 97516 : do_ener = qs_ot_env(1)%settings%do_ener
175 :
176 97516 : IF (PRESENT(restart_history)) THEN
177 84374 : restart = restart_history
178 : ELSE
179 13142 : CALL ot_mini_prepare(qs_ot_env, restart, para_env_inter_kp)
180 : END IF
181 97516 : IF (restart) THEN
182 17234 : DO ispin = 1, nspin
183 17234 : CALL qs_ot_reset_history(qs_ot_env(ispin))
184 : END DO
185 : reuse_gradient = .FALSE.
186 : END IF
187 :
188 97516 : npasses = 1
189 97516 : IF (PRESENT(matrix_hc_physical)) THEN
190 992 : CPASSERT(qs_ot_env(1)%settings%occupation_preconditioner)
191 992 : CPASSERT(SIZE(matrix_hc_physical) == nspin)
192 : npasses = 2
193 : END IF
194 :
195 97516 : qs_ot_env(1)%OT_METHOD_FULL = ""
196 :
197 : ! compute the gradient for the variables x
198 97516 : IF (.NOT. reuse_gradient .AND. .NOT. qs_ot_env(1)%energy_only) THEN
199 74970 : qs_ot_env(1)%gradient = 0.0_dp
200 74970 : local_gradient = .FALSE.
201 74970 : IF (do_ks .AND. qs_ot_env(1)%has_complex_kpoint_state) THEN
202 2410 : IF (.NOT. PRESENT(matrix_hc_im)) THEN
203 0 : CPABORT("Complex k-point OT derivative requires imaginary H(k)*C(k).")
204 : END IF
205 2410 : local_gradient = qs_ot_env(1)%para_env%num_pe == 1
206 2410 : IF (local_gradient) THEN
207 : CALL qs_ot_get_derivative_local_batch(qs_ot_env, matrix_hc, matrix_hc_im, &
208 1256 : matrix_hc_physical, matrix_hc_physical_im)
209 : END IF
210 : END IF
211 162702 : DO ispin = 1, nspin
212 87732 : IF (do_ks .AND. .NOT. local_gradient) THEN
213 170732 : DO pass = 1, npasses
214 85671 : hc_re => matrix_hc(ispin)%matrix
215 85671 : IF (pass == 2) hc_re => matrix_hc_physical(ispin)%matrix
216 85671 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
217 2386 : CPASSERT(PRESENT(matrix_hc_im))
218 2386 : hc_im => matrix_hc_im(ispin)%matrix
219 2386 : IF (pass == 2) THEN
220 610 : CPASSERT(PRESENT(matrix_hc_physical_im))
221 610 : hc_im => matrix_hc_physical_im(ispin)%matrix
222 : END IF
223 3690 : SELECT CASE (qs_ot_env(1)%settings%ot_algorithm)
224 : CASE ("TOD")
225 1304 : CALL qs_ot_get_derivative_complex(hc_re, hc_im, qs_ot_env(ispin))
226 : CASE ("REF")
227 1082 : CALL qs_ot_get_derivative_ref_complex(hc_re, hc_im, qs_ot_env(ispin))
228 : CASE DEFAULT
229 2386 : CPABORT("Complex k-point OT derivative requires ALGORITHM STRICT or IRAC")
230 : END SELECT
231 : ELSE
232 163038 : SELECT CASE (qs_ot_env(1)%settings%ot_algorithm)
233 : CASE ("TOD")
234 : CALL qs_ot_get_derivative(hc_re, qs_ot_env(ispin)%matrix_x, &
235 : qs_ot_env(ispin)%matrix_sx, &
236 79753 : qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin))
237 : CASE ("REF")
238 : CALL qs_ot_get_derivative_ref(hc_re, &
239 : qs_ot_env(ispin)%matrix_x, qs_ot_env(ispin)%matrix_sx, &
240 3532 : qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin))
241 : CASE DEFAULT
242 83285 : CPABORT("ALGORITHM NYI")
243 : END SELECT
244 : END IF
245 170732 : IF (pass < npasses) THEN
246 : ! Save the search gradient; the final pass supplies physical X/rotation gradients.
247 610 : CPASSERT(ASSOCIATED(qs_ot_env(ispin)%matrix_preconditioned_gx))
248 610 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_preconditioned_gx, qs_ot_env(ispin)%matrix_gx)
249 610 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
250 610 : CPASSERT(ASSOCIATED(qs_ot_env(ispin)%matrix_preconditioned_gx_im))
251 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_preconditioned_gx_im, &
252 610 : qs_ot_env(ispin)%matrix_gx_im)
253 : END IF
254 : END IF
255 : END DO
256 : END IF
257 : ! and also the gradient along the direction
258 162702 : IF (qs_ot_env(1)%use_dx) THEN
259 82764 : IF (do_ks) THEN
260 82764 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_dx, tmp)
261 82764 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient + tmp
262 82764 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
263 4395 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, qs_ot_env(ispin)%matrix_dx_im, tmp)
264 4395 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient + tmp
265 : END IF
266 82764 : IF (qs_ot_env(1)%settings%do_rotation) THEN
267 2969 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_dx, tmp)
268 2969 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient + 0.5_dp*tmp
269 2969 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
270 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
271 1373 : qs_ot_env(ispin)%rot_mat_dx_im, tmp)
272 1373 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient + 0.5_dp*tmp
273 : END IF
274 : END IF
275 : END IF
276 82764 : IF (do_ener) THEN
277 8322 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_dx)
278 966 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient + tmp
279 : END IF
280 : ELSE
281 4968 : IF (do_ks) THEN
282 4968 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_gx, tmp)
283 4968 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient - tmp
284 4968 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
285 52 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, qs_ot_env(ispin)%matrix_gx_im, tmp)
286 52 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient - tmp
287 : END IF
288 4968 : IF (qs_ot_env(1)%settings%do_rotation) THEN
289 0 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_gx, tmp)
290 0 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient - 0.5_dp*tmp
291 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
292 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
293 0 : qs_ot_env(ispin)%rot_mat_gx_im, tmp)
294 0 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient - 0.5_dp*tmp
295 : END IF
296 : END IF
297 : END IF
298 4968 : IF (do_ener) THEN
299 0 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_gx)
300 0 : qs_ot_env(1)%gradient = qs_ot_env(1)%gradient - tmp
301 : END IF
302 : END IF
303 : END DO
304 74970 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, qs_ot_env(1)%gradient)
305 : END IF
306 :
307 97516 : IF (evaluate_gradient_only) THEN
308 648 : CALL timestop(handle)
309 648 : RETURN
310 : END IF
311 :
312 141306 : SELECT CASE (qs_ot_env(1)%settings%OT_METHOD)
313 : CASE ("CG")
314 44438 : IF (current_point_is_fine(qs_ot_env)) THEN
315 23132 : qs_ot_env(1)%OT_METHOD_FULL = "OT CG"
316 23132 : CALL ot_new_cg_direction(qs_ot_env, para_env_inter_kp)
317 23132 : qs_ot_env(1)%line_search_count = 0
318 : ELSE
319 21306 : qs_ot_env(1)%OT_METHOD_FULL = "OT LS"
320 : END IF
321 44438 : CALL do_line_search(qs_ot_env)
322 : CASE ("SD")
323 164 : IF (current_point_is_fine(qs_ot_env)) THEN
324 50 : qs_ot_env(1)%OT_METHOD_FULL = "OT SD"
325 50 : CALL ot_new_sd_direction(qs_ot_env, para_env_inter_kp)
326 50 : qs_ot_env(1)%line_search_count = 0
327 : ELSE
328 114 : qs_ot_env(1)%OT_METHOD_FULL = "OT LS"
329 : END IF
330 164 : CALL do_line_search(qs_ot_env)
331 : CASE ("DIIS")
332 51136 : qs_ot_env(1)%OT_METHOD_FULL = "OT DIIS"
333 51136 : CALL ot_diis_step(qs_ot_env, para_env_inter_kp)
334 : CASE ("BROY")
335 192 : qs_ot_env(1)%OT_METHOD_FULL = "OT BROY"
336 192 : CALL ot_broyden_step(qs_ot_env, para_env_inter_kp)
337 : CASE ("LBFG")
338 938 : IF (current_point_is_fine(qs_ot_env)) THEN
339 460 : qs_ot_env(1)%OT_METHOD_FULL = "OT LBFGS"
340 460 : CALL ot_new_lbfgs_direction(qs_ot_env, para_env_inter_kp)
341 460 : qs_ot_env(1)%line_search_count = 0
342 : ELSE
343 478 : qs_ot_env(1)%OT_METHOD_FULL = "OT LS"
344 : END IF
345 938 : CALL do_line_search(qs_ot_env)
346 : CASE DEFAULT
347 96868 : CPABORT("OT_METHOD NYI")
348 : END SELECT
349 :
350 96868 : CALL timestop(handle)
351 :
352 : END SUBROUTINE ot_mini
353 :
354 : ! **************************************************************************************************
355 : !> \brief Prepare projection metrics and synchronize pending history restarts before building gradients.
356 : !> \param qs_ot_env OT channel environments
357 : !> \param restart_history whether ot_mini must restart and evaluate a gradient, even on an energy-only step
358 : !> \param para_env_inter_kp communicator between distributed k-point groups
359 : ! **************************************************************************************************
360 96868 : SUBROUTINE ot_mini_prepare(qs_ot_env, restart_history, para_env_inter_kp)
361 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
362 : LOGICAL, INTENT(OUT) :: restart_history
363 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
364 :
365 : INTEGER :: ispin, restart
366 :
367 96868 : restart = 0
368 96868 : CALL qs_ot_prepare_complex_tangent_metric_batch(qs_ot_env)
369 211388 : DO ispin = 1, SIZE(qs_ot_env)
370 114520 : IF (qs_ot_env(1)%settings%ks .AND. qs_ot_env(ispin)%settings%ot_algorithm == 'TOD' .AND. &
371 : qs_ot_env(ispin)%has_complex_kpoint_state) THEN
372 3314 : CALL qs_ot_prepare_complex_tangent_metric(qs_ot_env(ispin))
373 : END IF
374 211388 : IF (qs_ot_env(ispin)%preconditioner_changed) restart = 1
375 : END DO
376 96868 : IF (PRESENT(para_env_inter_kp)) THEN
377 3050 : IF (ASSOCIATED(para_env_inter_kp)) CALL para_env_inter_kp%max(restart)
378 : END IF
379 96868 : restart_history = restart /= 0
380 96868 : END SUBROUTINE ot_mini_prepare
381 :
382 : ! **************************************************************************************************
383 : !> \brief Evaluate the current OT derivative without advancing the minimizer.
384 : !> \param qs_ot_env OT channel environments
385 : !> \param matrix_hc real H*C products
386 : !> \param matrix_hc_im imaginary H*C products
387 : !> \param matrix_hc_physical occupation-weighted real H*C products
388 : !> \param matrix_hc_physical_im occupation-weighted imaginary H*C products
389 : !> \param para_env_inter_kp communicator between distributed K-point groups
390 : !> \param restart_history pending restart from ot_mini_prepare; absent prepares here
391 : ! **************************************************************************************************
392 648 : SUBROUTINE ot_mini_prepare_gradient(qs_ot_env, matrix_hc, matrix_hc_im, &
393 : matrix_hc_physical, matrix_hc_physical_im, para_env_inter_kp, &
394 : restart_history)
395 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
396 : TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: matrix_hc
397 : TYPE(dbcsr_p_type), DIMENSION(:), OPTIONAL, &
398 : POINTER :: matrix_hc_im, matrix_hc_physical, &
399 : matrix_hc_physical_im
400 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
401 : LOGICAL, INTENT(IN), OPTIONAL :: restart_history
402 :
403 : CALL ot_mini(qs_ot_env, matrix_hc, matrix_hc_im=matrix_hc_im, &
404 : matrix_hc_physical=matrix_hc_physical, &
405 : matrix_hc_physical_im=matrix_hc_physical_im, &
406 648 : para_env_inter_kp=para_env_inter_kp, gradient_only=.TRUE., restart_history=restart_history)
407 :
408 648 : END SUBROUTINE ot_mini_prepare_gradient
409 :
410 : ! **************************************************************************************************
411 : !> \brief Apply a channel's orbital preconditioner, or a scaled identity when absent.
412 : !> \param qs_ot_env current OT channel
413 : !> \param matrix_in input real part
414 : !> \param matrix_out output real part
415 : !> \param matrix_in_im input imaginary part, unused for real channels
416 : !> \param matrix_out_im output imaginary part, unused for real channels
417 : !> \param identity_scale scale used only when the preconditioner is absent
418 : ! **************************************************************************************************
419 87294 : SUBROUTINE ot_apply_channel_preconditioner(qs_ot_env, matrix_in, matrix_out, &
420 : matrix_in_im, matrix_out_im, identity_scale)
421 : TYPE(qs_ot_type), INTENT(IN) :: qs_ot_env
422 : TYPE(dbcsr_type), POINTER :: matrix_in, matrix_out, matrix_in_im, &
423 : matrix_out_im
424 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: identity_scale
425 :
426 : REAL(KIND=dp) :: scale
427 :
428 87294 : scale = 1.0_dp
429 87294 : IF (ASSOCIATED(qs_ot_env%preconditioner)) THEN
430 76696 : IF (qs_ot_env%has_complex_kpoint_state) THEN
431 3909 : CALL apply_preconditioner(qs_ot_env%preconditioner, matrix_in, matrix_in_im, matrix_out, matrix_out_im)
432 3909 : CPASSERT(qs_ot_env%kpoint_weight > 0.0_dp)
433 3909 : scale = qs_ot_kpoint_preconditioner_scale(qs_ot_env%kpoint_weight)
434 : ELSE
435 72787 : CALL apply_preconditioner(qs_ot_env%preconditioner, matrix_in, matrix_out)
436 : END IF
437 : ELSE
438 10598 : CALL dbcsr_copy(matrix_out, matrix_in)
439 10598 : IF (qs_ot_env%has_complex_kpoint_state) CALL dbcsr_copy(matrix_out_im, matrix_in_im)
440 10598 : IF (PRESENT(identity_scale)) scale = identity_scale
441 : END IF
442 87294 : IF (scale /= 1.0_dp) THEN
443 2205 : CALL dbcsr_scale(matrix_out, scale)
444 2205 : IF (qs_ot_env%has_complex_kpoint_state) CALL dbcsr_scale(matrix_out_im, scale)
445 : END IF
446 87294 : END SUBROUTINE ot_apply_channel_preconditioner
447 :
448 : ! **************************************************************************************************
449 : !> \brief Precondition orbital gradients across local channels, retaining the distributed fallback.
450 : !> \param qs_ot_env OT channels
451 : !> \param matrix_out output pointers, indexed by real/imaginary component and channel
452 : !> \param use_occupation_response override the channel's occupation-response setting
453 : ! **************************************************************************************************
454 74502 : SUBROUTINE ot_precondition_gradients(qs_ot_env, matrix_out, use_occupation_response)
455 : TYPE(qs_ot_type), INTENT(IN) :: qs_ot_env(:)
456 : TYPE(dbcsr_p_type), INTENT(IN) :: matrix_out(:, :)
457 : LOGICAL, INTENT(IN), OPTIONAL :: use_occupation_response
458 :
459 : INTEGER :: i
460 : LOGICAL :: handled, local, use_response
461 161536 : TYPE(preconditioner_p_type) :: preconditioners(SIZE(qs_ot_env))
462 335604 : TYPE(dbcsr_p_type) :: matrix_in(2, SIZE(qs_ot_env))
463 149004 : REAL(dp) :: scales(SIZE(qs_ot_env))
464 :
465 161536 : local = .TRUE.
466 161536 : scales(:) = 1.0_dp
467 161536 : DO i = 1, SIZE(qs_ot_env)
468 87034 : use_response = qs_ot_env(i)%settings%occupation_preconditioner
469 87034 : IF (PRESENT(use_occupation_response)) use_response = use_occupation_response
470 87034 : IF (use_response) THEN
471 522 : matrix_in(1, i)%matrix => qs_ot_env(i)%matrix_preconditioned_gx
472 522 : matrix_in(2, i)%matrix => qs_ot_env(i)%matrix_preconditioned_gx_im
473 : ELSE
474 86512 : matrix_in(1, i)%matrix => qs_ot_env(i)%matrix_gx
475 86512 : matrix_in(2, i)%matrix => qs_ot_env(i)%matrix_gx_im
476 : END IF
477 87034 : preconditioners(i)%preconditioner => qs_ot_env(i)%preconditioner
478 161536 : IF (qs_ot_env(i)%has_complex_kpoint_state) THEN
479 3761 : IF (ASSOCIATED(qs_ot_env(i)%preconditioner)) THEN
480 3705 : CPASSERT(qs_ot_env(i)%kpoint_weight > 0.0_dp)
481 3705 : scales(i) = qs_ot_kpoint_preconditioner_scale(qs_ot_env(i)%kpoint_weight)
482 : END IF
483 : ELSE
484 : local = .FALSE.
485 : END IF
486 : END DO
487 74502 : handled = .FALSE.
488 74502 : IF (local) CALL apply_preconditioner_complex_batch(preconditioners, matrix_in, matrix_out, handled, scales)
489 74502 : IF (handled) RETURN
490 157969 : DO i = 1, SIZE(qs_ot_env)
491 : CALL ot_apply_channel_preconditioner(qs_ot_env(i), matrix_in(1, i)%matrix, &
492 : matrix_out(1, i)%matrix, matrix_in(2, i)%matrix, &
493 157969 : matrix_out(2, i)%matrix)
494 : END DO
495 : END SUBROUTINE ot_precondition_gradients
496 :
497 : ! **************************************************************************************************
498 : !> \brief Sum a minimizer scalar over distributed k-point groups when requested.
499 : !> \param para_env_inter_kp communicator between distributed k-point groups
500 : !> \param value scalar to sum
501 : ! **************************************************************************************************
502 650111 : SUBROUTINE ot_mini_sum_kpoint_scalar(para_env_inter_kp, value)
503 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
504 : REAL(KIND=dp), INTENT(INOUT) :: value
505 :
506 650111 : IF (PRESENT(para_env_inter_kp)) THEN
507 21490 : IF (ASSOCIATED(para_env_inter_kp)) CALL para_env_inter_kp%sum(value)
508 : END IF
509 :
510 650111 : END SUBROUTINE ot_mini_sum_kpoint_scalar
511 :
512 : ! **************************************************************************************************
513 : !> \brief Normalize the convergence product on an orbital/energy scale.
514 : !> \param qs_ot_env local spin/k-point channels; updates delta in the first channel
515 : !> \param nvariables global coordinate count, weighted for complex k points
516 : !> \param para_env_inter_kp communicator between distributed k-point groups
517 : !> \param residual_product optional current, globally reduced, unscaled gradient/preconditioned-gradient product
518 : !> \note Call before overwriting the physical gradients for complex k points.
519 : ! **************************************************************************************************
520 74970 : SUBROUTINE ot_update_delta(qs_ot_env, nvariables, para_env_inter_kp, residual_product)
521 : TYPE(qs_ot_type), DIMENSION(:), INTENT(INOUT) :: qs_ot_env
522 : REAL(KIND=dp), INTENT(OUT) :: nvariables
523 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
524 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: residual_product
525 :
526 : INTEGER :: ispin, k, n
527 : INTEGER(KIND=int_8) :: nlocal, norbital
528 : REAL(KIND=dp) :: gnorm, weight
529 :
530 74970 : gnorm = qs_ot_env(1)%gnorm
531 74970 : IF (qs_ot_env(1)%has_complex_kpoint_state) THEN
532 2410 : gnorm = ot_kpoint_convergence(qs_ot_env, para_env_inter_kp, residual_product)
533 : END IF
534 :
535 : ! Rotations contribute to the product, but do not add coordinates to the
536 : ! orbital/energy normalization and dilute the stopping criterion.
537 74970 : nvariables = 0.0_dp
538 162702 : DO ispin = 1, SIZE(qs_ot_env)
539 87732 : nlocal = 0_int_8
540 87732 : IF (qs_ot_env(1)%settings%ks) THEN
541 87732 : CALL dbcsr_get_info(qs_ot_env(ispin)%matrix_x, nfullrows_total=n, nfullcols_total=k)
542 87732 : norbital = INT(n, KIND=int_8)*INT(k, KIND=int_8)
543 87732 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) norbital = 2_int_8*norbital
544 : nlocal = nlocal + norbital
545 : END IF
546 87732 : IF (qs_ot_env(1)%settings%do_ener) THEN
547 966 : nlocal = nlocal + SIZE(qs_ot_env(ispin)%ener_x, KIND=int_8)
548 : END IF
549 87732 : weight = 1.0_dp
550 87732 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) weight = qs_ot_env(ispin)%kpoint_weight
551 162702 : nvariables = nvariables + weight*REAL(nlocal, KIND=dp)
552 : END DO
553 74970 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, nvariables)
554 :
555 74970 : qs_ot_env(1)%delta = 0.0_dp
556 74970 : IF (nvariables > 0.0_dp) qs_ot_env(1)%delta = SQRT(ABS(gnorm)/nvariables)
557 74970 : END SUBROUTINE ot_update_delta
558 :
559 : ! **************************************************************************************************
560 : !> \brief Evaluate the coupled residual with one k-point weight per contribution.
561 : !> \param qs_ot_env complex k-point channels with their current physical gradients
562 : !> \param para_env_inter_kp communicator between distributed k-point groups
563 : !> \param residual_product optional current, globally reduced, unscaled residual product
564 : !> \return weighted convergence product, independent of search directions and history
565 : ! **************************************************************************************************
566 2410 : FUNCTION ot_kpoint_convergence(qs_ot_env, para_env_inter_kp, residual_product) RESULT(gnorm)
567 : TYPE(qs_ot_type), DIMENSION(:), INTENT(INOUT) :: qs_ot_env
568 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
569 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: residual_product
570 : REAL(KIND=dp) :: gnorm
571 :
572 : INTEGER, PARAMETER :: identity_residual = 3, &
573 : occupation_response = 1, &
574 : physical_residual = 2
575 :
576 : INTEGER :: ispin, residual_kind, reuse_product
577 : REAL(KIND=dp) :: inverse_weight, scale, tmp
578 : TYPE(dbcsr_type), POINTER :: input_im, input_re, residual_im, &
579 : residual_re
580 : TYPE(dbcsr_type), TARGET :: identity_im, identity_re
581 :
582 2410 : IF (PRESENT(residual_product)) THEN
583 1950 : reuse_product = 0
584 1950 : IF (ieee_is_finite(residual_product)) THEN
585 1950 : IF (residual_product > 0.0_dp) reuse_product = 1
586 : END IF
587 : ! Without an attached preconditioner, the history product lacks the inverse k-point weight.
588 5703 : DO ispin = 1, SIZE(qs_ot_env)
589 : IF (.NOT. ASSOCIATED(qs_ot_env(ispin)%preconditioner) .OR. &
590 : qs_ot_env(ispin)%settings%do_ener .OR. &
591 3753 : qs_ot_env(ispin)%settings%do_rotation .OR. &
592 2813 : qs_ot_env(ispin)%settings%occupation_preconditioner) reuse_product = 0
593 : END DO
594 : ! All groups must take the same branch before the fallback's collective reductions.
595 1950 : IF (PRESENT(para_env_inter_kp)) THEN
596 1462 : IF (ASSOCIATED(para_env_inter_kp)) CALL para_env_inter_kp%min(reuse_product)
597 : END IF
598 1950 : IF (reuse_product == 1) THEN
599 : gnorm = residual_product
600 : RETURN
601 : END IF
602 : END IF
603 :
604 984 : DO residual_kind = MERGE(occupation_response, physical_residual, &
605 978 : qs_ot_env(1)%settings%occupation_preconditioner), identity_residual
606 984 : gnorm = 0.0_dp
607 2551 : DO ispin = 1, SIZE(qs_ot_env)
608 1567 : CPASSERT(qs_ot_env(ispin)%kpoint_weight > 0.0_dp)
609 1567 : inverse_weight = 1.0_dp/qs_ot_env(ispin)%kpoint_weight
610 1567 : IF (qs_ot_env(1)%settings%ks) THEN
611 1567 : input_re => qs_ot_env(ispin)%matrix_gx
612 1567 : input_im => qs_ot_env(ispin)%matrix_gx_im
613 1567 : IF (residual_kind == occupation_response) THEN
614 726 : input_re => qs_ot_env(ispin)%matrix_preconditioned_gx
615 726 : input_im => qs_ot_env(ispin)%matrix_preconditioned_gx_im
616 : END IF
617 1567 : scale = inverse_weight
618 1567 : IF (residual_kind /= identity_residual) THEN
619 : ! Derivative workspaces are free here; preserve minimizer history and directions.
620 1567 : IF (qs_ot_env(ispin)%settings%ot_algorithm == 'TOD') THEN
621 572 : residual_re => qs_ot_env(ispin)%matrix_buf_nk
622 572 : residual_im => qs_ot_env(ispin)%matrix_buf_nk_im
623 : ELSE
624 995 : residual_re => qs_ot_env(ispin)%buf1_n_k
625 995 : residual_im => qs_ot_env(ispin)%buf1_n_k_dp
626 : END IF
627 : CALL ot_apply_channel_preconditioner(qs_ot_env(ispin), input_re, residual_re, &
628 1567 : input_im, residual_im)
629 : ! The attached inverse Hessian already includes 1/w; an identity map does not.
630 1567 : IF (ASSOCIATED(qs_ot_env(ispin)%preconditioner)) scale = 1.0_dp
631 : ELSE
632 : ! STRICT projection uses the derivative buffers, so this rare fallback needs
633 : ! separate targets. Only one channel's temporary matrices are live at a time.
634 0 : CALL dbcsr_copy(identity_re, input_re)
635 0 : CALL dbcsr_copy(identity_im, input_im)
636 0 : residual_re => identity_re
637 0 : residual_im => identity_im
638 0 : IF (qs_ot_env(ispin)%settings%ot_algorithm == 'TOD') THEN
639 : CALL qs_ot_project_gradient(qs_ot_env(ispin), residual_re, residual_im, &
640 0 : use_preconditioner=.FALSE.)
641 : END IF
642 : END IF
643 1567 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, residual_re, tmp)
644 1567 : gnorm = gnorm + scale*tmp
645 1567 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, residual_im, tmp)
646 1567 : gnorm = gnorm + scale*tmp
647 1567 : IF (residual_kind == identity_residual) THEN
648 0 : CALL dbcsr_release(identity_re)
649 0 : CALL dbcsr_release(identity_im)
650 : END IF
651 1567 : IF (qs_ot_env(ispin)%settings%do_rotation) THEN
652 1383 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_gx, tmp)
653 1383 : gnorm = gnorm + 0.5_dp*inverse_weight*tmp
654 1383 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, qs_ot_env(ispin)%rot_mat_gx_im, tmp)
655 1383 : gnorm = gnorm + 0.5_dp*inverse_weight*tmp
656 : END IF
657 : END IF
658 2551 : IF (qs_ot_env(1)%settings%do_ener) THEN
659 976 : IF (residual_kind == occupation_response) THEN
660 5442 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_preconditioned_gx)
661 : ELSE
662 2922 : tmp = inverse_weight*DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_gx)
663 : END IF
664 976 : gnorm = gnorm + tmp
665 : END IF
666 : END DO
667 984 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, gnorm)
668 984 : IF (ieee_is_finite(gnorm) .AND. gnorm > 0.0_dp) EXIT
669 : ! Decide the diagnostic fallback from the complete weighted product. The minimizers
670 : ! independently test their actual directions with the original, unscaled inner products.
671 : END DO
672 3388 : END FUNCTION ot_kpoint_convergence
673 :
674 : ! **************************************************************************************************
675 : !> \brief Assess a finite-response candidate at its accepted Mermin endpoint.
676 : !> \param reference_energy Mermin energy at the start of the search
677 : !> \param current_energy Mermin energy at the accepted endpoint
678 : !> \param reference_residual physical residual at the start of the search
679 : !> \param current_residual physical residual at the accepted endpoint
680 : !> \param predicted_slope positive predicted decrease per unit line-search position
681 : !> \param predicted_curvature signed finite-response curvature
682 : !> \param position accepted line-search position
683 : !> \param default_step configured initial line-search position
684 : !> \param predicted_drop finite quadratic-model decrease
685 : !> \param measured_drop measured Mermin decrease
686 : !> \param quality measured over predicted decrease
687 : !> \param residual_ratio endpoint over reference residual
688 : !> \param good whether this is a resolved, useful accepted response sample
689 : ! **************************************************************************************************
690 20 : PURE SUBROUTINE ot_mermin_response_assess( &
691 : reference_energy, current_energy, reference_residual, current_residual, &
692 : predicted_slope, predicted_curvature, position, default_step, &
693 : predicted_drop, measured_drop, quality, residual_ratio, good)
694 : REAL(KIND=dp), INTENT(IN) :: reference_energy, current_energy, reference_residual, &
695 : current_residual, predicted_slope, predicted_curvature, position, default_step
696 : REAL(KIND=dp), INTENT(OUT) :: predicted_drop, measured_drop, quality, &
697 : residual_ratio
698 : LOGICAL, INTENT(OUT) :: good
699 :
700 : REAL(KIND=dp) :: accepted_position, resolution, scale
701 :
702 20 : predicted_drop = 0.0_dp
703 20 : measured_drop = 0.0_dp
704 20 : quality = 0.0_dp
705 20 : residual_ratio = HUGE(1.0_dp)
706 20 : good = .FALSE.
707 : IF (.NOT. ieee_is_finite(reference_energy) .OR. .NOT. ieee_is_finite(current_energy) .OR. &
708 : .NOT. ieee_is_finite(reference_residual) .OR. .NOT. ieee_is_finite(current_residual) .OR. &
709 : .NOT. ieee_is_finite(predicted_slope) .OR. .NOT. ieee_is_finite(predicted_curvature) .OR. &
710 20 : .NOT. ieee_is_finite(position) .OR. .NOT. ieee_is_finite(default_step)) RETURN
711 :
712 20 : accepted_position = ABS(position)
713 20 : measured_drop = reference_energy - current_energy
714 : predicted_drop = accepted_position*predicted_slope - &
715 20 : 0.5_dp*accepted_position**2*predicted_curvature
716 : scale = MAX(1.0_dp, ABS(reference_energy), ABS(current_energy), &
717 20 : ABS(predicted_drop), ABS(measured_drop))
718 20 : resolution = 256.0_dp*EPSILON(1.0_dp)*scale
719 20 : IF (reference_residual > TINY(reference_residual)) THEN
720 20 : residual_ratio = MAX(0.0_dp, current_residual)/reference_residual
721 : END IF
722 20 : IF (predicted_drop > resolution) quality = measured_drop/predicted_drop
723 :
724 : good = predicted_drop > resolution .AND. measured_drop > resolution .AND. &
725 : accepted_position >= 1.0E-6_dp*MAX(ABS(default_step), TINY(default_step)) .AND. &
726 26 : quality >= 0.05_dp .AND. quality <= 5.0_dp .AND. residual_ratio <= 1.0_dp
727 :
728 : END SUBROUTINE ot_mermin_response_assess
729 :
730 : ! **************************************************************************************************
731 : !> \brief Recover the total finite Mermin curvature of an accepted line-search secant.
732 : !>
733 : !> For F(alpha)=F(0)-g*alpha+0.5*kappa*alpha**2, the accepted energy difference fixes
734 : !> kappa without separating REF, rotation, occupation, and self-consistent Hxc terms.
735 : !> \param reference_energy Mermin energy at the start of the accepted search
736 : !> \param current_energy Mermin energy at its accepted endpoint
737 : !> \param predicted_slope positive directional decrease at the search origin
738 : !> \param position accepted line-search position
739 : !> \param curvature recovered total finite curvature
740 : !> \param valid whether a resolved finite curvature was recovered
741 : ! **************************************************************************************************
742 138 : PURE SUBROUTINE ot_mermin_secant_curvature( &
743 : reference_energy, current_energy, predicted_slope, position, curvature, valid)
744 : REAL(KIND=dp), INTENT(IN) :: reference_energy, current_energy, &
745 : predicted_slope, position
746 : REAL(KIND=dp), INTENT(OUT) :: curvature
747 : LOGICAL, INTENT(OUT) :: valid
748 :
749 : REAL(KIND=dp) :: accepted_position, measured_drop
750 :
751 138 : curvature = 0.0_dp
752 138 : valid = .FALSE.
753 : IF (.NOT. ieee_is_finite(reference_energy) .OR. .NOT. ieee_is_finite(current_energy) .OR. &
754 138 : .NOT. ieee_is_finite(predicted_slope) .OR. .NOT. ieee_is_finite(position)) RETURN
755 138 : accepted_position = ABS(position)
756 138 : IF (accepted_position <= SQRT(EPSILON(1.0_dp))) RETURN
757 :
758 138 : measured_drop = reference_energy - current_energy
759 138 : curvature = 2.0_dp*(accepted_position*predicted_slope - measured_drop)/accepted_position**2
760 138 : valid = ieee_is_finite(curvature)
761 138 : IF (.NOT. valid) curvature = 0.0_dp
762 :
763 : END SUBROUTINE ot_mermin_secant_curvature
764 :
765 : ! **************************************************************************************************
766 : !> \brief Compare an endpoint response shadow with the linear accepted-step model.
767 : !> \param reference_energy Mermin energy at the start of the search
768 : !> \param current_energy Mermin energy at the accepted endpoint
769 : !> \param reference_residual physical residual at the start of the search
770 : !> \param current_residual physical residual at the accepted endpoint
771 : !> \param predicted_slope positive reference directional decrease
772 : !> \param shadow_curvature endpoint finite-response curvature along the accepted direction
773 : !> \param position accepted line-search position
774 : !> \param advantage reduction of symmetric prediction error relative to the linear model
775 : !> \param residual_ratio endpoint over reference residual
776 : !> \param good whether the passive response prediction is both better and residual-consistent
777 : ! **************************************************************************************************
778 144 : PURE SUBROUTINE ot_mermin_response_compare( &
779 : reference_energy, current_energy, reference_residual, current_residual, &
780 : predicted_slope, shadow_curvature, position, advantage, residual_ratio, good)
781 : REAL(KIND=dp), INTENT(IN) :: reference_energy, current_energy, reference_residual, &
782 : current_residual, predicted_slope, shadow_curvature, position
783 : REAL(KIND=dp), INTENT(OUT) :: advantage, residual_ratio
784 : LOGICAL, INTENT(OUT) :: good
785 :
786 : REAL(KIND=dp) :: accepted_position, baseline_drop, baseline_error, candidate_drop, &
787 : candidate_error, measured_drop, resolution, scale
788 :
789 144 : advantage = 0.0_dp
790 144 : residual_ratio = HUGE(1.0_dp)
791 144 : good = .FALSE.
792 : IF (.NOT. ieee_is_finite(reference_energy) .OR. .NOT. ieee_is_finite(current_energy) .OR. &
793 : .NOT. ieee_is_finite(reference_residual) .OR. .NOT. ieee_is_finite(current_residual) .OR. &
794 144 : .NOT. ieee_is_finite(predicted_slope) .OR. .NOT. ieee_is_finite(shadow_curvature) .OR. &
795 : .NOT. ieee_is_finite(position)) RETURN
796 :
797 144 : accepted_position = ABS(position)
798 144 : measured_drop = reference_energy - current_energy
799 144 : baseline_drop = accepted_position*predicted_slope
800 144 : candidate_drop = baseline_drop - 0.5_dp*accepted_position**2*shadow_curvature
801 : scale = MAX(1.0_dp, ABS(reference_energy), ABS(current_energy), ABS(measured_drop), &
802 144 : ABS(baseline_drop), ABS(candidate_drop))
803 144 : resolution = 256.0_dp*EPSILON(1.0_dp)*scale
804 144 : IF (reference_residual > TINY(reference_residual)) THEN
805 144 : residual_ratio = MAX(0.0_dp, current_residual)/reference_residual
806 : END IF
807 144 : IF (measured_drop <= resolution .OR. baseline_drop <= resolution .OR. &
808 : candidate_drop <= resolution) RETURN
809 :
810 144 : baseline_error = ABS(measured_drop - baseline_drop)/scale
811 144 : candidate_error = ABS(measured_drop - candidate_drop)/scale
812 144 : advantage = baseline_error - candidate_error
813 160 : good = advantage > 64.0_dp*EPSILON(1.0_dp)*scale .AND. residual_ratio <= 1.05_dp
814 :
815 : END SUBROUTINE ot_mermin_response_compare
816 :
817 : ! **************************************************************************************************
818 : !> \brief Compare response and conventional directions in one accepted Mermin model.
819 : !>
820 : !> The most recent accepted conventional secant supplies the dimensionless local curvature
821 : !> ratio kappa/g. Applying that ratio to the current conventional slope transfers the
822 : !> measured line-search model without assuming that the two directions have equal norms.
823 : !> Both candidates are then evaluated at the same line-search position.
824 : !> \param baseline_slope positive current conventional directional decrease
825 : !> \param response_slope positive current response directional decrease
826 : !> \param response_curvature current finite-response curvature
827 : !> \param accepted_slope positive slope of the preceding accepted conventional search
828 : !> \param accepted_curvature measured total curvature of that accepted search
829 : !> \param position common line-search position used for comparison
830 : !> \param baseline_drop predicted conventional Mermin decrease
831 : !> \param response_drop predicted response Mermin decrease
832 : !> \param relative_gain response improvement relative to the larger predicted decrease
833 : !> \param preferred whether the response model predicts a resolved improvement
834 : ! **************************************************************************************************
835 32 : PURE SUBROUTINE ot_mermin_response_candidate_preferred( &
836 : baseline_slope, response_slope, response_curvature, accepted_slope, accepted_curvature, &
837 : position, baseline_drop, response_drop, relative_gain, preferred)
838 : REAL(KIND=dp), INTENT(IN) :: baseline_slope, response_slope, &
839 : response_curvature, accepted_slope, &
840 : accepted_curvature, position
841 : REAL(KIND=dp), INTENT(OUT) :: baseline_drop, response_drop, &
842 : relative_gain
843 : LOGICAL, INTENT(OUT) :: preferred
844 :
845 : REAL(KIND=dp) :: alpha, baseline_curvature, &
846 : curvature_ratio, resolution, scale
847 :
848 32 : baseline_drop = 0.0_dp
849 32 : response_drop = 0.0_dp
850 32 : relative_gain = 0.0_dp
851 32 : preferred = .FALSE.
852 : IF (.NOT. ieee_is_finite(baseline_slope) .OR. .NOT. ieee_is_finite(response_slope) .OR. &
853 : .NOT. ieee_is_finite(response_curvature) .OR. .NOT. ieee_is_finite(accepted_slope) .OR. &
854 32 : .NOT. ieee_is_finite(accepted_curvature) .OR. .NOT. ieee_is_finite(position)) RETURN
855 :
856 32 : alpha = ABS(position)
857 : scale = MAX(1.0_dp, ABS(baseline_slope), ABS(response_slope), &
858 32 : ABS(response_curvature), ABS(accepted_slope), ABS(accepted_curvature))
859 32 : resolution = 256.0_dp*EPSILON(1.0_dp)*scale
860 : IF (alpha <= SQRT(EPSILON(1.0_dp)) .OR. baseline_slope <= resolution .OR. &
861 32 : response_slope <= resolution .OR. accepted_slope <= resolution) RETURN
862 :
863 32 : curvature_ratio = accepted_curvature/accepted_slope
864 32 : baseline_curvature = curvature_ratio*baseline_slope
865 32 : baseline_drop = alpha*baseline_slope - 0.5_dp*alpha**2*baseline_curvature
866 32 : response_drop = alpha*response_slope - 0.5_dp*alpha**2*response_curvature
867 32 : scale = MAX(ABS(baseline_drop), ABS(response_drop), resolution)
868 32 : relative_gain = (response_drop - baseline_drop)/scale
869 44 : preferred = response_drop > resolution .AND. response_drop > baseline_drop + resolution
870 :
871 : END SUBROUTINE ot_mermin_response_candidate_preferred
872 :
873 : ! **************************************************************************************************
874 : !> \brief Select a sparse coupled-response probe from accepted-history evidence.
875 : !> \param available whether every local channel has a finite coupled response
876 : !> \param residual current conventional-preconditioned residual
877 : !> \param directions number of accepted directions seen by this response state
878 : !> \param shadow_good_samples passive accepted steps where response improved the prediction
879 : !> \param good_samples consecutive useful active response samples
880 : !> \param cooldown accepted directions remaining after a failed response sample
881 : !> \return whether the next direction should use the coupled response candidate
882 : ! **************************************************************************************************
883 23592 : PURE FUNCTION ot_mermin_response_probe( &
884 : available, residual, directions, shadow_good_samples, good_samples, cooldown) RESULT(probe)
885 : LOGICAL, INTENT(IN) :: available
886 : REAL(KIND=dp), INTENT(IN) :: residual
887 : INTEGER, INTENT(IN) :: directions, shadow_good_samples, &
888 : good_samples, cooldown
889 : LOGICAL :: probe
890 :
891 : INTEGER :: interval, phase
892 :
893 23592 : probe = .FALSE.
894 23592 : IF (.NOT. available .OR. .NOT. ieee_is_finite(residual)) RETURN
895 120 : IF (residual <= 0.0_dp .OR. residual > 2.0E-3_dp .OR. cooldown > 0) RETURN
896 120 : IF (shadow_good_samples < 3) RETURN
897 58 : interval = 8
898 58 : IF (good_samples >= 3) interval = 4
899 58 : IF (directions < interval) RETURN
900 36 : phase = MOD(directions, interval)
901 36 : probe = phase == 0 .OR. phase == 1
902 :
903 36 : END FUNCTION ot_mermin_response_probe
904 :
905 : ! **************************************************************************************************
906 : !> \brief Decide whether the next accepted state needs the dense finite Mermin response.
907 : !>
908 : !> A pending shadow is always completed. Otherwise the response is prepared while collecting
909 : !> the initial accepted-step calibration and at the state immediately preceding a sparse
910 : !> response-probe window. The relaxed residual bound accounts for using the preceding
911 : !> accepted residual before the current norm has been reduced across K-point groups.
912 : !> \param residual preceding accepted conventional-preconditioned residual
913 : !> \param directions number of accepted directions already seen
914 : !> \param shadow_good_samples useful passive accepted-step samples
915 : !> \param good_samples useful active response samples
916 : !> \param cooldown accepted directions remaining after a failed active sample
917 : !> \param shadow_pending whether the current endpoint must assess a prepared shadow
918 : !> \return whether to build the dense finite response at the current accepted endpoint
919 : ! **************************************************************************************************
920 396 : PURE FUNCTION ot_mermin_response_preparation_needed( &
921 : residual, directions, shadow_good_samples, good_samples, cooldown, shadow_pending) RESULT(prepare)
922 : REAL(KIND=dp), INTENT(IN) :: residual
923 : INTEGER, INTENT(IN) :: directions, shadow_good_samples, &
924 : good_samples, cooldown
925 : LOGICAL, INTENT(IN) :: shadow_pending
926 : LOGICAL :: prepare
927 :
928 : INTEGER :: interval, next_direction, phase
929 :
930 396 : prepare = shadow_pending
931 396 : IF (prepare) RETURN
932 252 : IF (.NOT. ieee_is_finite(residual) .OR. residual <= 0.0_dp .OR. residual > 4.0E-3_dp) RETURN
933 176 : IF (cooldown > 1) RETURN
934 160 : IF (shadow_good_samples < 3) THEN
935 396 : prepare = .TRUE.
936 : RETURN
937 : END IF
938 :
939 86 : interval = 8
940 86 : IF (good_samples >= 3) interval = 4
941 86 : next_direction = MIN(HUGE(directions) - 1, MAX(0, directions) + 1)
942 86 : phase = MOD(next_direction, interval)
943 86 : prepare = phase == interval - 1
944 :
945 86 : END FUNCTION ot_mermin_response_preparation_needed
946 :
947 : ! **************************************************************************************************
948 : !> \brief Decide whether a prepared conventional direction needs a shadow at its endpoint.
949 : !> \param residual current conventional-preconditioned residual
950 : !> \param directions number of accepted directions including the current one
951 : !> \param shadow_good_samples useful passive accepted-step samples
952 : !> \param good_samples useful active response samples
953 : !> \param cooldown accepted directions remaining after a failed active sample
954 : !> \return whether the next accepted endpoint must assess the current response shadow
955 : ! **************************************************************************************************
956 172 : PURE FUNCTION ot_mermin_response_shadow_followup( &
957 : residual, directions, shadow_good_samples, good_samples, cooldown) RESULT(followup)
958 : REAL(KIND=dp), INTENT(IN) :: residual
959 : INTEGER, INTENT(IN) :: directions, shadow_good_samples, &
960 : good_samples, cooldown
961 : LOGICAL :: followup
962 :
963 : INTEGER :: interval, phase
964 :
965 172 : followup = .FALSE.
966 172 : IF (.NOT. ieee_is_finite(residual) .OR. residual <= 0.0_dp .OR. residual > 4.0E-3_dp) RETURN
967 172 : IF (cooldown > 1) RETURN
968 172 : IF (shadow_good_samples < 3) THEN
969 172 : followup = .TRUE.
970 : RETURN
971 : END IF
972 :
973 60 : interval = 8
974 60 : IF (good_samples >= 3) interval = 4
975 60 : phase = MOD(MAX(0, directions), interval)
976 60 : followup = phase == interval - 1 .OR. phase == 0
977 :
978 : END FUNCTION ot_mermin_response_shadow_followup
979 :
980 : ! **************************************************************************************************
981 : !> \brief Assess and optionally select a calibrated finite Mermin response direction.
982 : !>
983 : !> The caller supplies its conventional descent direction in the product workspaces. A
984 : !> finite-response direction is considered only after accepted conventional steps have
985 : !> calibrated its quadratic model. This keeps the response independent of the line-search
986 : !> minimizer while preserving the physical residual as the convergence measure.
987 : !> \param qs_ot_env OT environments for all local spin/k-point channels
988 : !> \param para_env_inter_kp communicator between distributed k-point groups
989 : !> \param baseline_delta conventional preconditioned residual
990 : !> \param test_down physical gradient dotted into the conventional direction, updated on selection
991 : !> \param use_response_candidate whether the finite-response direction was selected
992 : ! **************************************************************************************************
993 23592 : SUBROUTINE ot_try_mermin_response_direction( &
994 : qs_ot_env, para_env_inter_kp, baseline_delta, test_down, use_response_candidate)
995 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
996 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
997 : REAL(KIND=dp), INTENT(IN) :: baseline_delta
998 : REAL(KIND=dp), INTENT(INOUT) :: test_down
999 : LOGICAL, INTENT(OUT) :: use_response_candidate
1000 :
1001 : INTEGER :: ispin, nspin
1002 : LOGICAL :: active_candidate_available, candidate_available, candidate_good, do_ener, do_ks, &
1003 : probe_response_candidate, shadow_good
1004 : REAL(KIND=dp) :: baseline_model_drop, measured_drop, predicted_drop, response_advantage, &
1005 : response_model_drop, response_quality, response_relative_gain, response_residual_ratio, &
1006 : response_test_down, tmp
1007 : TYPE(cp_logger_type), POINTER :: logger
1008 :
1009 23592 : nspin = SIZE(qs_ot_env)
1010 23592 : do_ks = qs_ot_env(1)%settings%ks
1011 23592 : do_ener = qs_ot_env(1)%settings%do_ener
1012 23592 : logger => cp_get_default_logger()
1013 23592 : shadow_good = .FALSE.
1014 23592 : candidate_good = .FALSE.
1015 :
1016 23592 : IF (qs_ot_env(1)%response_shadow_pending) THEN
1017 : CALL ot_mermin_response_compare( &
1018 : qs_ot_env(1)%response_reference_energy, qs_ot_env(1)%etotal, &
1019 : qs_ot_env(1)%response_reference_residual, baseline_delta, &
1020 : qs_ot_env(1)%response_predicted_slope, qs_ot_env(1)%response_shadow_curvature, &
1021 144 : qs_ot_env(1)%ds_min, response_advantage, response_residual_ratio, shadow_good)
1022 144 : IF (shadow_good) THEN
1023 : qs_ot_env(1)%response_shadow_good_samples = &
1024 : MIN(HUGE(qs_ot_env(1)%response_shadow_good_samples) - 1, &
1025 128 : qs_ot_env(1)%response_shadow_good_samples + 1)
1026 : ELSE
1027 : qs_ot_env(1)%response_shadow_good_samples = &
1028 16 : MAX(0, qs_ot_env(1)%response_shadow_good_samples - 1)
1029 : END IF
1030 144 : IF (logger%iter_info%print_level >= high_print_level .AND. logger%para_env%is_source()) THEN
1031 : WRITE (cp_logger_get_default_unit_nr(logger), &
1032 : '(A,1X,I5,2(1X,L1),4(1X,ES16.8))') &
1033 0 : " OT Mermin response shadow good hxc-dir advantage residual model shadow:", &
1034 0 : qs_ot_env(1)%response_shadow_good_samples, shadow_good, &
1035 0 : qs_ot_env(1)%response_hxc_direction_valid, response_advantage, &
1036 0 : response_residual_ratio, qs_ot_env(1)%response_model_curvature, &
1037 0 : qs_ot_env(1)%response_shadow_curvature
1038 : END IF
1039 144 : qs_ot_env(1)%response_shadow_pending = .FALSE.
1040 : END IF
1041 :
1042 23592 : IF (qs_ot_env(1)%response_candidate_pending) THEN
1043 : CALL ot_mermin_response_assess( &
1044 : qs_ot_env(1)%response_reference_energy, qs_ot_env(1)%etotal, &
1045 : qs_ot_env(1)%response_reference_residual, baseline_delta, &
1046 : qs_ot_env(1)%response_predicted_slope, qs_ot_env(1)%response_predicted_curvature, &
1047 : qs_ot_env(1)%ds_min, qs_ot_env(1)%settings%ds_min, predicted_drop, measured_drop, &
1048 16 : response_quality, response_residual_ratio, candidate_good)
1049 16 : IF (candidate_good) THEN
1050 : qs_ot_env(1)%response_candidate_good_samples = &
1051 : MIN(HUGE(qs_ot_env(1)%response_candidate_good_samples) - 1, &
1052 12 : qs_ot_env(1)%response_candidate_good_samples + 1)
1053 : ELSE
1054 : qs_ot_env(1)%response_candidate_good_samples = &
1055 4 : MAX(0, qs_ot_env(1)%response_candidate_good_samples - 2)
1056 4 : qs_ot_env(1)%response_candidate_cooldown = 6
1057 : END IF
1058 16 : IF (logger%iter_info%print_level >= high_print_level .AND. logger%para_env%is_source()) THEN
1059 : WRITE (cp_logger_get_default_unit_nr(logger), '(A,1X,I5,1X,L1,4(1X,ES16.8))') &
1060 0 : " OT Mermin response candidate good quality residual predicted measured:", &
1061 0 : qs_ot_env(1)%response_candidate_good_samples, candidate_good, response_quality, &
1062 0 : response_residual_ratio, predicted_drop, measured_drop
1063 : END IF
1064 16 : qs_ot_env(1)%response_candidate_pending = .FALSE.
1065 : END IF
1066 :
1067 : qs_ot_env(1)%response_candidate_directions = &
1068 : MIN(HUGE(qs_ot_env(1)%response_candidate_directions) - 1, &
1069 23592 : qs_ot_env(1)%response_candidate_directions + 1)
1070 23592 : IF (qs_ot_env(1)%response_candidate_cooldown > 0) THEN
1071 24 : qs_ot_env(1)%response_candidate_cooldown = qs_ot_env(1)%response_candidate_cooldown - 1
1072 : END IF
1073 :
1074 : candidate_available = qs_ot_env(1)%settings%occupation_preconditioner .AND. &
1075 23592 : qs_ot_env(1)%settings%do_rotation .AND. do_ener
1076 52838 : DO ispin = 1, nspin
1077 : candidate_available = candidate_available .AND. &
1078 : qs_ot_env(ispin)%rotation_response_valid .AND. &
1079 : ASSOCIATED(qs_ot_env(ispin)%matrix_response_gx) .AND. &
1080 : ASSOCIATED(qs_ot_env(ispin)%matrix_response_gx_im) .AND. &
1081 : ASSOCIATED(qs_ot_env(ispin)%rot_mat_response_gx) .AND. &
1082 : ASSOCIATED(qs_ot_env(ispin)%rot_mat_response_gx_im) .AND. &
1083 81814 : ASSOCIATED(qs_ot_env(ispin)%ener_response_gx)
1084 : END DO
1085 : active_candidate_available = candidate_available .AND. shadow_good .AND. &
1086 23592 : qs_ot_env(1)%response_hxc_direction_valid
1087 : probe_response_candidate = ot_mermin_response_probe( &
1088 : active_candidate_available, baseline_delta, &
1089 : qs_ot_env(1)%response_candidate_directions, &
1090 : qs_ot_env(1)%response_shadow_good_samples, &
1091 : qs_ot_env(1)%response_candidate_good_samples, &
1092 23592 : qs_ot_env(1)%response_candidate_cooldown)
1093 23592 : use_response_candidate = .FALSE.
1094 :
1095 23592 : IF (probe_response_candidate) THEN
1096 32 : response_test_down = 0.0_dp
1097 32 : baseline_model_drop = 0.0_dp
1098 32 : response_model_drop = 0.0_dp
1099 32 : response_relative_gain = 0.0_dp
1100 32 : IF (do_ks) THEN
1101 76 : DO ispin = 1, nspin
1102 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, &
1103 44 : qs_ot_env(ispin)%matrix_response_gx, tmp)
1104 44 : response_test_down = response_test_down - tmp
1105 44 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1106 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, &
1107 44 : qs_ot_env(ispin)%matrix_response_gx_im, tmp)
1108 44 : response_test_down = response_test_down - tmp
1109 : END IF
1110 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, &
1111 44 : qs_ot_env(ispin)%rot_mat_response_gx, tmp)
1112 44 : response_test_down = response_test_down - 0.5_dp*tmp
1113 120 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1114 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
1115 44 : qs_ot_env(ispin)%rot_mat_response_gx_im, tmp)
1116 44 : response_test_down = response_test_down - 0.5_dp*tmp
1117 : END IF
1118 : END DO
1119 : END IF
1120 32 : IF (do_ener) THEN
1121 76 : DO ispin = 1, nspin
1122 : response_test_down = response_test_down - &
1123 : DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, &
1124 292 : qs_ot_env(ispin)%ener_response_gx)
1125 : END DO
1126 : END IF
1127 32 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, response_test_down)
1128 32 : IF (response_test_down < 0.0_dp) THEN
1129 : CALL ot_mermin_response_candidate_preferred( &
1130 : -test_down, -response_test_down, qs_ot_env(1)%response_model_curvature, &
1131 : qs_ot_env(1)%response_predicted_slope, qs_ot_env(1)%response_shadow_curvature, &
1132 : qs_ot_env(1)%ds_min, baseline_model_drop, response_model_drop, &
1133 32 : response_relative_gain, use_response_candidate)
1134 : END IF
1135 32 : IF (logger%iter_info%print_level >= high_print_level .AND. logger%para_env%is_source()) THEN
1136 : WRITE (cp_logger_get_default_unit_nr(logger), '(A,4(1X,I5),5(1X,ES16.8),2(1X,L1))') &
1137 : " OT Mermin response candidate comparison directions shadow active cooldown residual "// &
1138 0 : "curvature baseline response gain probe use:", &
1139 0 : qs_ot_env(1)%response_candidate_directions, &
1140 0 : qs_ot_env(1)%response_shadow_good_samples, &
1141 0 : qs_ot_env(1)%response_candidate_good_samples, &
1142 0 : qs_ot_env(1)%response_candidate_cooldown, baseline_delta, &
1143 0 : qs_ot_env(1)%response_model_curvature, baseline_model_drop, response_model_drop, &
1144 0 : response_relative_gain, probe_response_candidate, use_response_candidate
1145 : END IF
1146 32 : IF (use_response_candidate) THEN
1147 48 : DO ispin = 1, nspin
1148 28 : IF (do_ks) THEN
1149 28 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_dx, qs_ot_env(ispin)%matrix_response_gx)
1150 28 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_dx, -1.0_dp)
1151 28 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1152 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_dx_im, &
1153 28 : qs_ot_env(ispin)%matrix_response_gx_im)
1154 28 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_dx_im, -1.0_dp)
1155 : END IF
1156 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx, &
1157 28 : qs_ot_env(ispin)%rot_mat_response_gx)
1158 28 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_dx, -1.0_dp)
1159 28 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1160 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx_im, &
1161 28 : qs_ot_env(ispin)%rot_mat_response_gx_im)
1162 28 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_dx_im, -1.0_dp)
1163 : END IF
1164 : END IF
1165 184 : IF (do_ener) qs_ot_env(ispin)%ener_dx = -qs_ot_env(ispin)%ener_response_gx
1166 : END DO
1167 20 : test_down = response_test_down
1168 : END IF
1169 : END IF
1170 :
1171 23592 : qs_ot_env(1)%response_reference_energy = qs_ot_env(1)%etotal
1172 23592 : qs_ot_env(1)%response_reference_residual = baseline_delta
1173 23592 : IF (test_down < 0.0_dp) THEN
1174 22920 : qs_ot_env(1)%response_predicted_slope = -test_down
1175 : ELSE
1176 672 : qs_ot_env(1)%response_predicted_slope = qs_ot_env(1)%gnorm
1177 : END IF
1178 23592 : IF (use_response_candidate) THEN
1179 20 : qs_ot_env(1)%response_candidate_pending = .TRUE.
1180 20 : qs_ot_env(1)%response_shadow_pending = .FALSE.
1181 20 : qs_ot_env(1)%response_predicted_curvature = qs_ot_env(1)%response_model_curvature
1182 : ELSE
1183 23572 : qs_ot_env(1)%response_candidate_pending = .FALSE.
1184 : qs_ot_env(1)%response_shadow_pending = candidate_available .AND. &
1185 : ot_mermin_response_shadow_followup( &
1186 : baseline_delta, &
1187 : qs_ot_env(1)%response_candidate_directions, &
1188 : qs_ot_env(1)%response_shadow_good_samples, &
1189 : qs_ot_env(1)%response_candidate_good_samples, &
1190 46998 : qs_ot_env(1)%response_candidate_cooldown)
1191 : END IF
1192 :
1193 23592 : END SUBROUTINE ot_try_mermin_response_direction
1194 :
1195 : !
1196 : ! checks if the current point is a good point for finding a new direction
1197 : ! or if we should improve the line_search, if it is used
1198 : !
1199 : ! **************************************************************************************************
1200 : !> \brief ...
1201 : !> \param qs_ot_env ...
1202 : !> \return ...
1203 : ! **************************************************************************************************
1204 45540 : FUNCTION current_point_is_fine(qs_ot_env) RESULT(res)
1205 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1206 : LOGICAL :: res
1207 :
1208 45540 : res = .FALSE.
1209 :
1210 : ! only if we have a gradient it can be fine
1211 45540 : IF (.NOT. qs_ot_env(1)%energy_only) THEN
1212 :
1213 : ! we have not yet started with the line search
1214 23642 : IF (qs_ot_env(1)%line_search_count == 0) THEN
1215 45540 : res = .TRUE.
1216 : RETURN
1217 : END IF
1218 :
1219 20396 : IF (qs_ot_env(1)%line_search_might_be_done) THEN
1220 : ! here we put the more complicated logic later
1221 20396 : res = .TRUE.
1222 20396 : RETURN
1223 : END IF
1224 :
1225 : END IF
1226 :
1227 : END FUNCTION current_point_is_fine
1228 :
1229 : !
1230 : ! performs various kinds of line searches
1231 : !
1232 : ! **************************************************************************************************
1233 : !> \brief ...
1234 : !> \param qs_ot_env ...
1235 : ! **************************************************************************************************
1236 45540 : SUBROUTINE do_line_search(qs_ot_env)
1237 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1238 :
1239 45968 : SELECT CASE (qs_ot_env(1)%settings%line_search_method)
1240 : CASE ("GOLD")
1241 428 : CALL do_line_search_gold(qs_ot_env)
1242 : CASE ("3PNT")
1243 1656 : CALL do_line_search_3pnt(qs_ot_env)
1244 : CASE ("2PNT")
1245 43366 : IF (use_three_point_mermin_search(qs_ot_env)) THEN
1246 1136 : CALL do_line_search_3pnt(qs_ot_env)
1247 : ELSE
1248 42230 : CALL do_line_search_2pnt(qs_ot_env)
1249 : END IF
1250 : CASE ("ADPT")
1251 80 : CALL do_line_search_adapt(qs_ot_env)
1252 : CASE ("NONE")
1253 10 : CALL do_line_search_none(qs_ot_env)
1254 : CASE DEFAULT
1255 45540 : CPABORT("NYI")
1256 : END SELECT
1257 45540 : END SUBROUTINE do_line_search
1258 :
1259 : ! **************************************************************************************************
1260 : !> \brief Add an energy-only guard point to strongly preconditioned complex Mermin steps.
1261 : !> \param qs_ot_env OT environments
1262 : !> \return Whether to use the guarded three-point search
1263 : ! **************************************************************************************************
1264 44270 : FUNCTION use_three_point_mermin_search(qs_ot_env) RESULT(res)
1265 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1266 : LOGICAL :: res
1267 :
1268 : INTEGER :: ispin
1269 :
1270 44270 : res = .FALSE.
1271 44270 : IF (.NOT. qs_ot_env(1)%settings%occupation_preconditioner) RETURN
1272 :
1273 1500 : DO ispin = 1, SIZE(qs_ot_env)
1274 1500 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1275 44270 : res = .TRUE.
1276 : RETURN
1277 : END IF
1278 : END DO
1279 : END FUNCTION use_three_point_mermin_search
1280 :
1281 : ! **************************************************************************************************
1282 : !> \brief moves x adding the right amount (ds) of the gradient or search direction
1283 : !> \param ds ...
1284 : !> \param qs_ot_env ...
1285 : !> \par History
1286 : !> 08.2004 created [ Joost VandeVondele ] copied here from a larger number of subroutines
1287 : ! **************************************************************************************************
1288 45540 : SUBROUTINE take_step(ds, qs_ot_env)
1289 : REAL(KIND=dp) :: ds
1290 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1291 :
1292 : CHARACTER(len=*), PARAMETER :: routineN = 'take_step'
1293 :
1294 : INTEGER :: handle, ispin, nspin
1295 : LOGICAL :: do_ener, do_ks
1296 :
1297 45540 : CALL timeset(routineN, handle)
1298 :
1299 45540 : nspin = SIZE(qs_ot_env)
1300 :
1301 45540 : do_ks = qs_ot_env(1)%settings%ks
1302 45540 : do_ener = qs_ot_env(1)%settings%do_ener
1303 :
1304 : ! now update x to take into account this new step
1305 : ! either dx or -gx is the direction to use
1306 45540 : IF (qs_ot_env(1)%use_dx) THEN
1307 45452 : IF (do_ks) THEN
1308 101460 : DO ispin = 1, nspin
1309 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_x, qs_ot_env(ispin)%matrix_dx, &
1310 56008 : alpha_scalar=1.0_dp, beta_scalar=ds)
1311 56008 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1312 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_x_im, qs_ot_env(ispin)%matrix_dx_im, &
1313 3603 : alpha_scalar=1.0_dp, beta_scalar=ds)
1314 : END IF
1315 101460 : IF (qs_ot_env(ispin)%settings%do_rotation) THEN
1316 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_x, qs_ot_env(ispin)%rot_mat_dx, &
1317 4963 : alpha_scalar=1.0_dp, beta_scalar=ds)
1318 4963 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1319 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_x_im, qs_ot_env(ispin)%rot_mat_dx_im, &
1320 2369 : alpha_scalar=1.0_dp, beta_scalar=ds)
1321 : END IF
1322 : END IF
1323 : END DO
1324 : END IF
1325 45452 : IF (do_ener) THEN
1326 2982 : DO ispin = 1, nspin
1327 14510 : qs_ot_env(ispin)%ener_x = qs_ot_env(ispin)%ener_x + ds*qs_ot_env(ispin)%ener_dx
1328 : END DO
1329 : END IF
1330 : ELSE
1331 88 : IF (do_ks) THEN
1332 176 : DO ispin = 1, nspin
1333 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_x, qs_ot_env(ispin)%matrix_gx, &
1334 88 : alpha_scalar=1.0_dp, beta_scalar=-ds)
1335 88 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1336 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_x_im, qs_ot_env(ispin)%matrix_gx_im, &
1337 0 : alpha_scalar=1.0_dp, beta_scalar=-ds)
1338 : END IF
1339 176 : IF (qs_ot_env(ispin)%settings%do_rotation) THEN
1340 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_x, qs_ot_env(ispin)%rot_mat_gx, &
1341 0 : alpha_scalar=1.0_dp, beta_scalar=-ds)
1342 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1343 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_x_im, qs_ot_env(ispin)%rot_mat_gx_im, &
1344 0 : alpha_scalar=1.0_dp, beta_scalar=-ds)
1345 : END IF
1346 : END IF
1347 : END DO
1348 : END IF
1349 88 : IF (do_ener) THEN
1350 0 : DO ispin = 1, nspin
1351 0 : qs_ot_env(ispin)%ener_x = qs_ot_env(ispin)%ener_x - ds*qs_ot_env(ispin)%ener_gx
1352 : END DO
1353 : END IF
1354 : END IF
1355 45540 : CALL timestop(handle)
1356 45540 : END SUBROUTINE take_step
1357 :
1358 : ! implements a golden ratio search as a robust way of minimizing
1359 : ! **************************************************************************************************
1360 : !> \brief ...
1361 : !> \param qs_ot_env ...
1362 : ! **************************************************************************************************
1363 428 : SUBROUTINE do_line_search_gold(qs_ot_env)
1364 :
1365 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1366 :
1367 : CHARACTER(len=*), PARAMETER :: routineN = 'do_line_search_gold'
1368 : REAL(KIND=dp), PARAMETER :: gold_sec = 0.3819_dp
1369 :
1370 : INTEGER :: count, handle
1371 : REAL(KIND=dp) :: ds
1372 :
1373 428 : CALL timeset(routineN, handle)
1374 :
1375 428 : qs_ot_env(1)%line_search_count = qs_ot_env(1)%line_search_count + 1
1376 428 : count = qs_ot_env(1)%line_search_count
1377 428 : qs_ot_env(1)%line_search_might_be_done = .FALSE.
1378 428 : qs_ot_env(1)%energy_only = .TRUE.
1379 :
1380 428 : IF (count + 1 > SIZE(qs_ot_env(1)%OT_pos)) THEN
1381 : ! should not happen, we pass with a warning first
1382 : ! you can increase the size of OT_pos and the like in qs_ot_env
1383 0 : CPABORT("MAX ITER EXCEEDED : FATAL")
1384 : END IF
1385 :
1386 428 : IF (qs_ot_env(1)%line_search_count == 1) THEN
1387 54 : qs_ot_env(1)%line_search_left = 1
1388 54 : qs_ot_env(1)%line_search_right = 0
1389 54 : qs_ot_env(1)%line_search_mid = 1
1390 54 : qs_ot_env(1)%ot_pos(1) = 0.0_dp
1391 54 : qs_ot_env(1)%ot_energy(1) = qs_ot_env(1)%etotal
1392 54 : qs_ot_env(1)%ot_pos(2) = qs_ot_env(1)%ds_min/gold_sec
1393 : ELSE
1394 374 : qs_ot_env(1)%ot_energy(count) = qs_ot_env(1)%etotal
1395 : ! it's essentially a book keeping game.
1396 : ! keep left on the left, keep (bring) right on the right
1397 : ! and mid in between these two
1398 374 : IF (qs_ot_env(1)%line_search_right == 0) THEN ! we do not yet have the right bracket
1399 64 : IF (qs_ot_env(1)%ot_energy(count - 1) < qs_ot_env(1)%ot_energy(count)) THEN
1400 42 : qs_ot_env(1)%line_search_right = count
1401 : qs_ot_env(1)%ot_pos(count + 1) = qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid) + &
1402 : (qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_right) - &
1403 42 : qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid))*gold_sec
1404 : ELSE
1405 22 : qs_ot_env(1)%line_search_left = qs_ot_env(1)%line_search_mid
1406 22 : qs_ot_env(1)%line_search_mid = count
1407 22 : qs_ot_env(1)%ot_pos(count + 1) = qs_ot_env(1)%ot_pos(count)/gold_sec ! expand
1408 : END IF
1409 : ELSE
1410 : ! first determine where we are and construct the new triplet
1411 310 : IF (qs_ot_env(1)%ot_pos(count) < qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid)) THEN
1412 126 : IF (qs_ot_env(1)%ot_energy(count) < qs_ot_env(1)%ot_energy(qs_ot_env(1)%line_search_mid)) THEN
1413 42 : qs_ot_env(1)%line_search_right = qs_ot_env(1)%line_search_mid
1414 42 : qs_ot_env(1)%line_search_mid = count
1415 : ELSE
1416 84 : qs_ot_env(1)%line_search_left = count
1417 : END IF
1418 : ELSE
1419 184 : IF (qs_ot_env(1)%ot_energy(count) < qs_ot_env(1)%ot_energy(qs_ot_env(1)%line_search_mid)) THEN
1420 76 : qs_ot_env(1)%line_search_left = qs_ot_env(1)%line_search_mid
1421 76 : qs_ot_env(1)%line_search_mid = count
1422 : ELSE
1423 108 : qs_ot_env(1)%line_search_right = count
1424 : END IF
1425 : END IF
1426 : ! now find the new point in the largest section
1427 310 : IF ((qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_right) &
1428 : - qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid)) > &
1429 : (qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid) &
1430 : - qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_left))) THEN
1431 : qs_ot_env(1)%ot_pos(count + 1) = &
1432 : qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid) + &
1433 : gold_sec*(qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_right) &
1434 162 : - qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid))
1435 : ELSE
1436 : qs_ot_env(1)%ot_pos(count + 1) = &
1437 : qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_left) + &
1438 : gold_sec*(qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid) &
1439 148 : - qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_left))
1440 : END IF
1441 : ! check for termination
1442 : IF (((qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_right) &
1443 : - qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid)) < &
1444 310 : qs_ot_env(1)%ds_min*qs_ot_env(1)%settings%gold_target) .AND. &
1445 : ((qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid) &
1446 : - qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_left)) < &
1447 : qs_ot_env(1)%ds_min*qs_ot_env(1)%settings%gold_target)) THEN
1448 42 : qs_ot_env(1)%energy_only = .FALSE.
1449 42 : qs_ot_env(1)%line_search_might_be_done = .TRUE.
1450 : END IF
1451 : END IF
1452 : END IF
1453 428 : ds = qs_ot_env(1)%OT_pos(count + 1) - qs_ot_env(1)%OT_pos(count)
1454 428 : qs_ot_env(1)%ds_min = qs_ot_env(1)%OT_pos(count + 1)
1455 :
1456 428 : CALL take_step(ds, qs_ot_env)
1457 :
1458 428 : CALL timestop(handle)
1459 :
1460 428 : END SUBROUTINE do_line_search_gold
1461 :
1462 : ! **************************************************************************************************
1463 : !> \brief ...
1464 : !> \param qs_ot_env ...
1465 : ! **************************************************************************************************
1466 2792 : SUBROUTINE do_line_search_3pnt(qs_ot_env)
1467 :
1468 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1469 :
1470 : CHARACTER(len=*), PARAMETER :: routineN = 'do_line_search_3pnt'
1471 :
1472 : INTEGER :: best_index, count, handle, i
1473 : REAL(KIND=dp) :: best_energy, denom, ds, fa, fb, fc, nom, &
1474 : pos, tol, val, xa, xb, xc
1475 :
1476 2792 : CALL timeset(routineN, handle)
1477 :
1478 2792 : qs_ot_env(1)%line_search_might_be_done = .FALSE.
1479 2792 : qs_ot_env(1)%energy_only = .TRUE.
1480 :
1481 : ! a three point interpolation based on the energy
1482 2792 : qs_ot_env(1)%line_search_count = qs_ot_env(1)%line_search_count + 1
1483 2792 : count = qs_ot_env(1)%line_search_count
1484 2792 : qs_ot_env(1)%ot_energy(count) = qs_ot_env(1)%etotal
1485 984 : SELECT CASE (count)
1486 : CASE (1)
1487 984 : qs_ot_env(1)%ot_pos(count) = 0.0_dp
1488 984 : qs_ot_env(1)%ot_pos(count + 1) = qs_ot_env(1)%ds_min*0.8_dp
1489 : CASE (2)
1490 904 : IF (qs_ot_env(1)%OT_energy(count) > qs_ot_env(1)%OT_energy(count - 1)) THEN
1491 88 : qs_ot_env(1)%OT_pos(count + 1) = qs_ot_env(1)%ds_min*0.5_dp
1492 : ELSE
1493 816 : qs_ot_env(1)%OT_pos(count + 1) = qs_ot_env(1)%ds_min*1.4_dp
1494 : END IF
1495 : CASE (3)
1496 904 : xa = qs_ot_env(1)%OT_pos(1)
1497 904 : xb = qs_ot_env(1)%OT_pos(2)
1498 904 : xc = qs_ot_env(1)%OT_pos(3)
1499 904 : fa = qs_ot_env(1)%OT_energy(1)
1500 904 : fb = qs_ot_env(1)%OT_energy(2)
1501 904 : fc = qs_ot_env(1)%OT_energy(3)
1502 904 : nom = (xb - xa)**2*(fb - fc) - (xb - xc)**2*(fb - fa)
1503 904 : denom = (xb - xa)*(fb - fc) - (xb - xc)*(fb - fa)
1504 904 : IF (ABS(denom) <= 1.0E-18_dp*MAX(ABS(fb - fc), ABS(fb - fa))) THEN
1505 : pos = xb
1506 : ELSE
1507 904 : pos = xb - 0.5_dp*nom/denom ! position of the stationary point
1508 : END IF
1509 : val = (pos - xa)*(pos - xb)*fc/((xc - xa)*(xc - xb)) + &
1510 : (pos - xb)*(pos - xc)*fa/((xa - xb)*(xa - xc)) + &
1511 904 : (pos - xc)*(pos - xa)*fb/((xb - xc)*(xb - xa))
1512 904 : best_index = 1
1513 2712 : DO i = 2, count
1514 2712 : IF (qs_ot_env(1)%OT_energy(i) < qs_ot_env(1)%OT_energy(best_index)) best_index = i
1515 : END DO
1516 904 : best_energy = qs_ot_env(1)%OT_energy(best_index)
1517 904 : tol = 10.0_dp*EPSILON(1.0_dp)*MAX(1.0_dp, ABS(best_energy))
1518 904 : IF (use_three_point_mermin_search(qs_ot_env) .AND. best_index /= 1 .AND. &
1519 : val >= best_energy - tol) THEN
1520 6 : qs_ot_env(1)%OT_pos(count + 1) = qs_ot_env(1)%OT_pos(best_index)
1521 898 : ELSE IF (val < fa .AND. val <= fb .AND. val <= fc) THEN ! OK, we go to a minimum
1522 : ! we take a guard against too large steps
1523 : qs_ot_env(1)%OT_pos(count + 1) = MAX(MAXVAL(qs_ot_env(1)%OT_pos(1:3))*0.01_dp, &
1524 3584 : MIN(pos, MAXVAL(qs_ot_env(1)%OT_pos(1:3))*4.0_dp))
1525 : ELSE ! just take an extended step
1526 8 : qs_ot_env(1)%OT_pos(count + 1) = MAXVAL(qs_ot_env(1)%OT_pos(1:3))*2.0_dp
1527 : END IF
1528 904 : qs_ot_env(1)%energy_only = .FALSE.
1529 904 : qs_ot_env(1)%line_search_might_be_done = .TRUE.
1530 : CASE DEFAULT
1531 2792 : CPABORT("NYI")
1532 : END SELECT
1533 2792 : ds = qs_ot_env(1)%OT_pos(count + 1) - qs_ot_env(1)%OT_pos(count)
1534 2792 : qs_ot_env(1)%ds_min = qs_ot_env(1)%OT_pos(count + 1)
1535 :
1536 2792 : CALL take_step(ds, qs_ot_env)
1537 :
1538 2792 : CALL timestop(handle)
1539 :
1540 2792 : END SUBROUTINE do_line_search_3pnt
1541 :
1542 : ! **************************************************************************************************
1543 : !> \brief ...
1544 : !> \param qs_ot_env ...
1545 : ! **************************************************************************************************
1546 42230 : SUBROUTINE do_line_search_2pnt(qs_ot_env)
1547 :
1548 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1549 :
1550 : CHARACTER(len=*), PARAMETER :: routineN = 'do_line_search_2pnt'
1551 :
1552 : INTEGER :: count, handle
1553 : REAL(KIND=dp) :: a, b, c, ds, pos, val, x0, x1
1554 :
1555 42230 : CALL timeset(routineN, handle)
1556 :
1557 42230 : qs_ot_env(1)%line_search_might_be_done = .FALSE.
1558 42230 : qs_ot_env(1)%energy_only = .TRUE.
1559 :
1560 : ! a three point interpolation based on the energy
1561 42230 : qs_ot_env(1)%line_search_count = qs_ot_env(1)%line_search_count + 1
1562 42230 : count = qs_ot_env(1)%line_search_count
1563 42230 : qs_ot_env(1)%ot_energy(count) = qs_ot_env(1)%etotal
1564 22574 : SELECT CASE (count)
1565 : CASE (1)
1566 22574 : qs_ot_env(1)%ot_pos(count) = 0.0_dp
1567 22574 : qs_ot_env(1)%ot_grad(count) = qs_ot_env(1)%gradient
1568 22574 : qs_ot_env(1)%ot_pos(count + 1) = qs_ot_env(1)%ds_min*1.0_dp
1569 : CASE (2)
1570 19656 : x0 = 0.0_dp
1571 19656 : c = qs_ot_env(1)%ot_energy(1)
1572 19656 : b = qs_ot_env(1)%ot_grad(1)
1573 19656 : x1 = qs_ot_env(1)%ot_pos(2)
1574 19656 : a = (qs_ot_env(1)%ot_energy(2) - b*x1 - c)/(x1**2)
1575 19656 : IF (a <= 0.0_dp) a = 1.0E-15_dp
1576 19656 : pos = -b/(2.0_dp*a)
1577 19656 : val = a*pos**2 + b*pos + c
1578 19656 : qs_ot_env(1)%energy_only = .FALSE.
1579 19656 : qs_ot_env(1)%line_search_might_be_done = .TRUE.
1580 19656 : IF (val < qs_ot_env(1)%ot_energy(1) .AND. val <= qs_ot_env(1)%ot_energy(2)) THEN
1581 : ! we go to a minimum, but ...
1582 : ! we take a guard against too large steps
1583 : qs_ot_env(1)%OT_pos(count + 1) = MAX(MAXVAL(qs_ot_env(1)%OT_pos(1:2))*0.01_dp, &
1584 58758 : MIN(pos, MAXVAL(qs_ot_env(1)%OT_pos(1:2))*4.0_dp))
1585 : ELSE ! just take an extended step
1586 210 : qs_ot_env(1)%OT_pos(count + 1) = MAXVAL(qs_ot_env(1)%OT_pos(1:2))*2.0_dp
1587 : END IF
1588 : CASE DEFAULT
1589 42230 : CPABORT("NYI")
1590 : END SELECT
1591 42230 : ds = qs_ot_env(1)%OT_pos(count + 1) - qs_ot_env(1)%OT_pos(count)
1592 42230 : qs_ot_env(1)%ds_min = qs_ot_env(1)%OT_pos(count + 1)
1593 :
1594 42230 : CALL take_step(ds, qs_ot_env)
1595 :
1596 42230 : CALL timestop(handle)
1597 :
1598 42230 : END SUBROUTINE do_line_search_2pnt
1599 :
1600 : ! **************************************************************************************************
1601 : !> \brief ...
1602 : !> \param qs_ot_env ...
1603 : ! **************************************************************************************************
1604 80 : SUBROUTINE do_line_search_adapt(qs_ot_env)
1605 :
1606 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1607 :
1608 : CHARACTER(len=*), PARAMETER :: routineN = 'do_line_search_adapt'
1609 : REAL(KIND=dp), PARAMETER :: grow_factor = 2.0_dp, &
1610 : shrink_factor = 0.5_dp
1611 :
1612 : INTEGER :: count, handle, il, im, ir
1613 : REAL(KIND=dp) :: a, b, c, denom, ds, el, em, er, &
1614 : step_size, xl, xm, xr
1615 :
1616 80 : CALL timeset(routineN, handle)
1617 :
1618 80 : qs_ot_env(1)%line_search_count = qs_ot_env(1)%line_search_count + 1
1619 80 : count = qs_ot_env(1)%line_search_count
1620 80 : qs_ot_env(1)%line_search_might_be_done = .FALSE.
1621 80 : qs_ot_env(1)%energy_only = .TRUE.
1622 :
1623 80 : IF (count + 1 > SIZE(qs_ot_env(1)%OT_pos)) THEN
1624 : ! should not happen, we pass with a warning first
1625 : ! you can increase the size of OT_pos and the like in qs_ot_env
1626 0 : CPABORT("MAX ITER EXCEEDED : FATAL")
1627 : END IF
1628 :
1629 : ! Perform an adaptive linesearch
1630 80 : IF (qs_ot_env(1)%line_search_count == 1) THEN
1631 20 : qs_ot_env(1)%line_search_left = 1
1632 20 : qs_ot_env(1)%line_search_right = 0
1633 20 : qs_ot_env(1)%line_search_mid = 1
1634 20 : qs_ot_env(1)%ot_Pos(1) = 0.0_dp
1635 20 : qs_ot_env(1)%ot_energy(1) = qs_ot_env(1)%etotal
1636 20 : qs_ot_env(1)%ot_Pos(2) = qs_ot_env(1)%ds_min*grow_factor
1637 : ELSE
1638 60 : qs_ot_env(1)%ot_energy(count) = qs_ot_env(1)%etotal
1639 : ! it's essentially a book keeping game.
1640 : ! keep left on the left, keep (bring) right on the right
1641 : ! and mid in between these two
1642 60 : IF (qs_ot_env(1)%line_search_right == 0) THEN ! we do not yet have the right bracket
1643 28 : IF (qs_ot_env(1)%ot_energy(count - 1) < qs_ot_env(1)%ot_energy(count)) THEN
1644 16 : qs_ot_env(1)%line_search_right = count
1645 : qs_ot_env(1)%ot_Pos(count + 1) = qs_ot_env(1)%ot_Pos(qs_ot_env(1)%line_search_mid) + &
1646 : (qs_ot_env(1)%ot_Pos(qs_ot_env(1)%line_search_right) - &
1647 16 : qs_ot_env(1)%ot_Pos(qs_ot_env(1)%line_search_mid))*shrink_factor
1648 : ELSE
1649 : ! expand further
1650 12 : qs_ot_env(1)%line_search_left = qs_ot_env(1)%line_search_mid
1651 12 : qs_ot_env(1)%line_search_mid = count
1652 12 : qs_ot_env(1)%ot_Pos(count + 1) = qs_ot_env(1)%ot_Pos(count)*grow_factor
1653 : END IF
1654 : ELSE
1655 : ! first determine where we are and construct the new triplet
1656 32 : IF (qs_ot_env(1)%ot_pos(count) < qs_ot_env(1)%ot_pos(qs_ot_env(1)%line_search_mid)) THEN
1657 0 : IF (qs_ot_env(1)%ot_energy(count) < qs_ot_env(1)%ot_energy(qs_ot_env(1)%line_search_mid)) THEN
1658 0 : qs_ot_env(1)%line_search_right = qs_ot_env(1)%line_search_mid
1659 0 : qs_ot_env(1)%line_search_mid = count
1660 : ELSE
1661 0 : qs_ot_env(1)%line_search_left = count
1662 : END IF
1663 : ELSE
1664 32 : IF (qs_ot_env(1)%ot_energy(count) < qs_ot_env(1)%ot_energy(qs_ot_env(1)%line_search_mid)) THEN
1665 12 : qs_ot_env(1)%line_search_left = qs_ot_env(1)%line_search_mid
1666 12 : qs_ot_env(1)%line_search_mid = count
1667 : ELSE
1668 20 : qs_ot_env(1)%line_search_right = count
1669 : END IF
1670 : END IF
1671 32 : il = qs_ot_env(1)%line_search_left
1672 32 : im = qs_ot_env(1)%line_search_mid
1673 32 : ir = qs_ot_env(1)%line_search_right
1674 32 : xl = qs_ot_env(1)%OT_pos(il)
1675 32 : xm = qs_ot_env(1)%OT_pos(im)
1676 32 : xr = qs_ot_env(1)%OT_pos(ir)
1677 32 : el = qs_ot_env(1)%ot_energy(il)
1678 32 : em = qs_ot_env(1)%ot_energy(im)
1679 32 : er = qs_ot_env(1)%ot_energy(ir)
1680 32 : IF (em < el) THEN
1681 16 : IF (er < em) THEN
1682 : !extend search
1683 0 : qs_ot_env(1)%ot_Pos(count + 1) = qs_ot_env(1)%ot_Pos(ir)*grow_factor
1684 : ELSE
1685 : ! Cramer's rule
1686 16 : denom = (xl - xm)*(xl - xr)*(xm - xr)
1687 16 : a = (xr*(em - el) + xm*(el - er) + xl*(er - em))/denom
1688 16 : b = (xr**2*(el - em) + xm**2*(er - el) + xl**2*(em - er))/denom
1689 16 : c = (xm*xr*(xm - xr)*el + xr*xl*(xr - xl)*em + xr*xm*(xr - xm)*er)/denom
1690 :
1691 16 : IF (ABS(a) /= 0.0_dp) THEN
1692 16 : step_size = -b/(2.0_dp*a)
1693 : ELSE
1694 : step_size = 0.0_dp
1695 : END IF
1696 16 : CPASSERT(step_size >= 0.0_dp)
1697 16 : qs_ot_env(1)%ot_Pos(count + 1) = step_size
1698 16 : qs_ot_env(1)%line_search_might_be_done = .TRUE.
1699 16 : qs_ot_env(1)%energy_only = .FALSE.
1700 : END IF
1701 : ELSE
1702 : ! contract search
1703 : qs_ot_env(1)%ot_Pos(count + 1) = qs_ot_env(1)%ot_Pos(im) + &
1704 16 : (qs_ot_env(1)%ot_Pos(ir) - qs_ot_env(1)%ot_Pos(im))*shrink_factor
1705 : END IF
1706 :
1707 : END IF
1708 : END IF
1709 80 : ds = qs_ot_env(1)%OT_pos(count + 1) - qs_ot_env(1)%OT_pos(count)
1710 80 : qs_ot_env(1)%ds_min = qs_ot_env(1)%OT_pos(count + 1)
1711 :
1712 80 : CALL take_step(ds, qs_ot_env)
1713 :
1714 80 : CALL timestop(handle)
1715 :
1716 80 : END SUBROUTINE do_line_search_adapt
1717 :
1718 : ! **************************************************************************************************
1719 : !> \brief ...
1720 : !> \param qs_ot_env ...
1721 : ! **************************************************************************************************
1722 10 : SUBROUTINE do_line_search_none(qs_ot_env)
1723 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1724 :
1725 10 : CALL take_step(qs_ot_env(1)%ds_min, qs_ot_env)
1726 :
1727 10 : END SUBROUTINE do_line_search_none
1728 :
1729 : !
1730 : ! creates a new SD direction, using the preconditioner if associated
1731 : ! also updates the gradient for line search
1732 : !
1733 :
1734 : ! **************************************************************************************************
1735 : !> \brief ...
1736 : !> \param qs_ot_env ...
1737 : !> \param para_env_inter_kp communicator between distributed k-point groups
1738 : ! **************************************************************************************************
1739 50 : SUBROUTINE ot_new_sd_direction(qs_ot_env, para_env_inter_kp)
1740 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1741 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
1742 :
1743 : CHARACTER(len=*), PARAMETER :: routineN = 'ot_new_sd_direction'
1744 :
1745 : INTEGER :: handle, ispin, nspin
1746 : LOGICAL :: do_ener, do_ks, use_occupation_response
1747 : REAL(KIND=dp) :: nvariables, tmp
1748 : TYPE(cp_logger_type), POINTER :: logger
1749 : TYPE(dbcsr_type), POINTER :: orbital_residual, orbital_residual_im
1750 236 : TYPE(dbcsr_p_type) :: matrix_out(2, SIZE(qs_ot_env))
1751 :
1752 50 : CALL timeset(routineN, handle)
1753 :
1754 50 : nspin = SIZE(qs_ot_env)
1755 50 : do_ks = qs_ot_env(1)%settings%ks
1756 50 : do_ener = qs_ot_env(1)%settings%do_ener
1757 50 : use_occupation_response = qs_ot_env(1)%settings%occupation_preconditioner
1758 :
1759 50 : IF (do_ks .AND. qs_ot_env(1)%use_dx) THEN
1760 88 : DO ispin = 1, SIZE(qs_ot_env)
1761 50 : matrix_out(1, ispin)%matrix => qs_ot_env(ispin)%matrix_dx
1762 88 : matrix_out(2, ispin)%matrix => qs_ot_env(ispin)%matrix_dx_im
1763 : END DO
1764 : END IF
1765 :
1766 : ! Keep the physical gradients until the complete orbital/rotation/energy product is known.
1767 0 : DO
1768 50 : qs_ot_env(1)%gnorm = 0.0_dp
1769 50 : IF (do_ks) THEN
1770 50 : IF (qs_ot_env(1)%use_dx) CALL ot_precondition_gradients(qs_ot_env, matrix_out, use_occupation_response)
1771 112 : DO ispin = 1, nspin
1772 62 : IF (use_occupation_response) THEN
1773 24 : orbital_residual => qs_ot_env(ispin)%matrix_preconditioned_gx
1774 24 : orbital_residual_im => qs_ot_env(ispin)%matrix_preconditioned_gx_im
1775 : ELSE
1776 38 : orbital_residual => qs_ot_env(ispin)%matrix_gx
1777 38 : orbital_residual_im => qs_ot_env(ispin)%matrix_gx_im
1778 : END IF
1779 62 : IF (qs_ot_env(1)%use_dx) THEN
1780 50 : orbital_residual => qs_ot_env(ispin)%matrix_dx
1781 50 : orbital_residual_im => qs_ot_env(ispin)%matrix_dx_im
1782 : END IF
1783 62 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, orbital_residual, tmp)
1784 62 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + tmp
1785 112 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1786 24 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, orbital_residual_im, tmp)
1787 24 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + tmp
1788 : END IF
1789 : END DO
1790 50 : IF (qs_ot_env(1)%settings%do_rotation) THEN
1791 36 : DO ispin = 1, nspin
1792 24 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_gx, tmp)
1793 : ! The antisymmetric rotation stores each variable twice.
1794 24 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + 0.5_dp*tmp
1795 36 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1796 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
1797 24 : qs_ot_env(ispin)%rot_mat_gx_im, tmp)
1798 24 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + 0.5_dp*tmp
1799 : END IF
1800 : END DO
1801 : END IF
1802 : END IF
1803 50 : IF (do_ener) THEN
1804 36 : DO ispin = 1, nspin
1805 24 : IF (use_occupation_response) THEN
1806 72 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_preconditioned_gx)
1807 : ELSE
1808 0 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_gx)
1809 : END IF
1810 36 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + tmp
1811 : END DO
1812 : END IF
1813 50 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, qs_ot_env(1)%gnorm)
1814 :
1815 : ! Match CG and history residuals: reject the coupled response as a whole, across all k points.
1816 50 : IF (.NOT. use_occupation_response .OR. qs_ot_env(1)%gnorm > 0.0_dp) EXIT
1817 12 : use_occupation_response = .FALSE.
1818 : END DO
1819 50 : IF (qs_ot_env(1)%gnorm < 0.0_dp) THEN
1820 0 : logger => cp_get_default_logger()
1821 0 : WRITE (cp_logger_get_default_unit_nr(logger), *) "WARNING Preconditioner not positive definite !"
1822 : END IF
1823 :
1824 : CALL ot_update_delta(qs_ot_env, nvariables, para_env_inter_kp, &
1825 50 : residual_product=qs_ot_env(1)%gnorm)
1826 :
1827 : ! Detaching a preconditioner leaves the direction workspace in use.
1828 50 : IF (qs_ot_env(1)%use_dx) THEN
1829 38 : IF (do_ks) THEN
1830 88 : DO ispin = 1, nspin
1831 50 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_dx, -1.0_dp)
1832 50 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1833 24 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_dx_im, -1.0_dp)
1834 : END IF
1835 88 : IF (qs_ot_env(1)%settings%do_rotation) THEN
1836 24 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx, qs_ot_env(ispin)%rot_mat_gx)
1837 24 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_dx, -1.0_dp)
1838 24 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1839 24 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx_im, qs_ot_env(ispin)%rot_mat_gx_im)
1840 24 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_dx_im, -1.0_dp)
1841 : END IF
1842 : END IF
1843 : END DO
1844 : END IF
1845 38 : IF (do_ener) THEN
1846 36 : DO ispin = 1, nspin
1847 36 : IF (use_occupation_response) THEN
1848 72 : qs_ot_env(ispin)%ener_dx = -qs_ot_env(ispin)%ener_preconditioned_gx
1849 : ELSE
1850 0 : qs_ot_env(ispin)%ener_dx = -qs_ot_env(ispin)%ener_gx
1851 : END IF
1852 : END DO
1853 : END IF
1854 12 : ELSE IF (use_occupation_response) THEN
1855 0 : IF (do_ks) THEN
1856 0 : DO ispin = 1, nspin
1857 0 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_preconditioned_gx)
1858 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1859 0 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_gx_im, qs_ot_env(ispin)%matrix_preconditioned_gx_im)
1860 : END IF
1861 : END DO
1862 : END IF
1863 0 : IF (do_ener) THEN
1864 0 : DO ispin = 1, nspin
1865 0 : qs_ot_env(ispin)%ener_gx = qs_ot_env(ispin)%ener_preconditioned_gx
1866 : END DO
1867 : END IF
1868 : END IF
1869 :
1870 : ! Handling the case of no free variables to optimize
1871 50 : IF (nvariables > 0.0_dp) THEN
1872 48 : qs_ot_env(1)%gradient = -qs_ot_env(1)%gnorm
1873 : ELSE
1874 2 : qs_ot_env(1)%gradient = 0.0_dp
1875 : END IF
1876 :
1877 50 : CALL timestop(handle)
1878 :
1879 50 : END SUBROUTINE ot_new_sd_direction
1880 :
1881 : !
1882 : ! creates a new CG direction. Implements Polak-Ribierre variant
1883 : ! using the preconditioner if associated
1884 : ! also updates the gradient for line search
1885 : !
1886 : ! **************************************************************************************************
1887 : !> \brief ...
1888 : !> \param qs_ot_env ...
1889 : !> \param para_env_inter_kp communicator between distributed k-point groups
1890 : ! **************************************************************************************************
1891 23132 : SUBROUTINE ot_new_cg_direction(qs_ot_env, para_env_inter_kp)
1892 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
1893 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
1894 :
1895 : CHARACTER(len=*), PARAMETER :: routineN = 'ot_new_cg_direction'
1896 :
1897 : INTEGER :: handle, ispin, nspin
1898 : LOGICAL :: do_ener, do_ks, &
1899 : preceding_response_candidate, &
1900 : use_response_candidate
1901 : REAL(KIND=dp) :: baseline_delta, beta_pr, gnorm_cross, &
1902 : nvariables, test_down, tmp
1903 : TYPE(cp_logger_type), POINTER :: logger
1904 108788 : TYPE(dbcsr_p_type) :: matrix_out(2, SIZE(qs_ot_env))
1905 :
1906 : ! Only the physical low-rank subspace solve can become active. The frozen-H response remains a
1907 : ! shadow when that total projected Hessian is indefinite or unresolved.
1908 :
1909 23132 : CALL timeset(routineN, handle)
1910 :
1911 23132 : nspin = SIZE(qs_ot_env)
1912 23132 : logger => cp_get_default_logger()
1913 23132 : preceding_response_candidate = qs_ot_env(1)%response_candidate_pending
1914 :
1915 23132 : do_ks = qs_ot_env(1)%settings%ks
1916 23132 : do_ener = qs_ot_env(1)%settings%do_ener
1917 23132 : gnorm_cross = 0.0_dp
1918 23132 : IF (do_ks) THEN
1919 51684 : DO ispin = 1, nspin
1920 28552 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_gx_old, tmp)
1921 28552 : gnorm_cross = gnorm_cross + tmp
1922 51684 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1923 981 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, qs_ot_env(ispin)%matrix_gx_old_im, tmp)
1924 981 : gnorm_cross = gnorm_cross + tmp
1925 : END IF
1926 : END DO
1927 23132 : IF (qs_ot_env(1)%settings%do_rotation) THEN
1928 3087 : DO ispin = 1, nspin
1929 1633 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_gx_old, tmp)
1930 : ! added 0.5, because we have (antisymmetry) only half the number of variables
1931 1633 : gnorm_cross = gnorm_cross + 0.5_dp*tmp
1932 3087 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1933 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
1934 395 : qs_ot_env(ispin)%rot_mat_gx_old_im, tmp)
1935 395 : gnorm_cross = gnorm_cross + 0.5_dp*tmp
1936 : END IF
1937 : END DO
1938 : END IF
1939 : END IF
1940 23132 : IF (do_ener) THEN
1941 606 : DO ispin = 1, nspin
1942 2578 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_gx_old)
1943 606 : gnorm_cross = gnorm_cross + tmp
1944 : END DO
1945 : END IF
1946 23132 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, gnorm_cross)
1947 :
1948 23132 : qs_ot_env(1)%gnorm = 0.0_dp
1949 23132 : IF (do_ks) THEN
1950 51684 : DO ispin = 1, SIZE(qs_ot_env)
1951 28552 : matrix_out(1, ispin)%matrix => qs_ot_env(ispin)%matrix_gx_old
1952 51684 : matrix_out(2, ispin)%matrix => qs_ot_env(ispin)%matrix_gx_old_im
1953 : END DO
1954 23132 : CALL ot_precondition_gradients(qs_ot_env, matrix_out)
1955 51684 : DO ispin = 1, nspin
1956 28552 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_gx_old, tmp)
1957 28552 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + tmp
1958 51684 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1959 981 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, qs_ot_env(ispin)%matrix_gx_old_im, tmp)
1960 981 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + tmp
1961 : END IF
1962 : END DO
1963 23132 : IF (qs_ot_env(1)%settings%do_rotation) THEN
1964 3087 : DO ispin = 1, nspin
1965 1633 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old, qs_ot_env(ispin)%rot_mat_gx)
1966 1633 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_gx_old, tmp)
1967 : ! The antisymmetric rotation stores each variable twice.
1968 1633 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + 0.5_dp*tmp
1969 3087 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
1970 395 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old_im, qs_ot_env(ispin)%rot_mat_gx_im)
1971 395 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, qs_ot_env(ispin)%rot_mat_gx_old_im, tmp)
1972 395 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + 0.5_dp*tmp
1973 : END IF
1974 : END DO
1975 : END IF
1976 : END IF
1977 23132 : IF (do_ener) THEN
1978 606 : DO ispin = 1, nspin
1979 350 : IF (qs_ot_env(ispin)%settings%occupation_preconditioner) THEN
1980 2578 : qs_ot_env(ispin)%ener_gx_old = qs_ot_env(ispin)%ener_preconditioned_gx
1981 : ELSE
1982 0 : qs_ot_env(ispin)%ener_gx_old = qs_ot_env(ispin)%ener_gx
1983 : END IF
1984 2578 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_gx_old)
1985 606 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + tmp
1986 : END DO
1987 : END IF
1988 :
1989 23132 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, qs_ot_env(1)%gnorm)
1990 23132 : IF (.NOT. qs_ot_env(1)%settings%occupation_preconditioner .AND. &
1991 : qs_ot_env(1)%gnorm < 0.0_dp) THEN
1992 0 : WRITE (cp_logger_get_default_unit_nr(logger), *) "WARNING Preconditioner not positive definite !"
1993 : END IF
1994 :
1995 : ! Occupation-unweighted orbital and energy blocks are useful preconditioner inputs, but only
1996 : ! their coupled direction is relevant for Mermin descent. Fall back as a whole when that
1997 : ! direction is not descending; rejecting individual blocks would destroy the Schur coupling.
1998 23132 : IF (qs_ot_env(1)%settings%occupation_preconditioner .AND. qs_ot_env(1)%gnorm <= 0.0_dp) THEN
1999 2 : qs_ot_env(1)%gnorm = 0.0_dp
2000 2 : IF (do_ks) THEN
2001 2 : CALL ot_precondition_gradients(qs_ot_env, matrix_out, use_occupation_response=.FALSE.)
2002 6 : DO ispin = 1, nspin
2003 4 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_gx_old, tmp)
2004 4 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + tmp
2005 4 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2006 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, &
2007 4 : qs_ot_env(ispin)%matrix_gx_old_im, tmp)
2008 4 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + tmp
2009 : END IF
2010 10 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2011 4 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old, qs_ot_env(ispin)%rot_mat_gx)
2012 4 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_gx_old, tmp)
2013 4 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + 0.5_dp*tmp
2014 4 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2015 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old_im, &
2016 4 : qs_ot_env(ispin)%rot_mat_gx_im)
2017 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
2018 4 : qs_ot_env(ispin)%rot_mat_gx_old_im, tmp)
2019 4 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + 0.5_dp*tmp
2020 : END IF
2021 : END IF
2022 : END DO
2023 : END IF
2024 2 : IF (do_ener) THEN
2025 6 : DO ispin = 1, nspin
2026 12 : qs_ot_env(ispin)%ener_gx_old = qs_ot_env(ispin)%ener_gx
2027 : qs_ot_env(1)%gnorm = qs_ot_env(1)%gnorm + &
2028 : DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, &
2029 14 : qs_ot_env(ispin)%ener_gx_old)
2030 : END DO
2031 : END IF
2032 2 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, qs_ot_env(1)%gnorm)
2033 : END IF
2034 : ! Keep convergence reporting tied to the conventional preconditioned residual. The finite
2035 : ! response is an accepted-step candidate, not a redefinition of the physical stopping test.
2036 : CALL ot_update_delta(qs_ot_env, nvariables, para_env_inter_kp, &
2037 23132 : residual_product=qs_ot_env(1)%gnorm)
2038 23132 : baseline_delta = qs_ot_env(1)%delta
2039 : ! Preserve the physical gradients until the stopping test, then retain CG's
2040 : ! preconditioned gradients for its direction and line-search products.
2041 23132 : IF (do_ks .AND. .NOT. qs_ot_env(1)%settings%occupation_preconditioner) THEN
2042 51078 : DO ispin = 1, nspin
2043 51078 : IF (ASSOCIATED(qs_ot_env(ispin)%preconditioner)) THEN
2044 22616 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_gx_old)
2045 22616 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2046 627 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_gx_im, qs_ot_env(ispin)%matrix_gx_old_im)
2047 : END IF
2048 : END IF
2049 : END DO
2050 : END IF
2051 23132 : IF (nvariables > 0.0_dp) THEN
2052 23000 : beta_pr = (qs_ot_env(1)%gnorm - gnorm_cross)/qs_ot_env(1)%gnorm_old
2053 : ELSE
2054 132 : beta_pr = 0.0_dp
2055 : END IF
2056 23132 : IF (preceding_response_candidate) beta_pr = 0.0_dp
2057 23132 : IF (cg_history_restart_required( &
2058 : qs_ot_env(1)%settings%occupation_preconditioner, qs_ot_env(1)%etotal, &
2059 2 : qs_ot_env(1)%response_reference_energy)) beta_pr = 0.0_dp
2060 23132 : beta_pr = MAX(beta_pr, 0.0_dp) ! reset to SD
2061 :
2062 23132 : test_down = 0.0_dp
2063 23132 : IF (do_ks) THEN
2064 51684 : DO ispin = 1, nspin
2065 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx, qs_ot_env(ispin)%matrix_gx_old, &
2066 28552 : alpha_scalar=beta_pr, beta_scalar=-1.0_dp)
2067 28552 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_dx, tmp)
2068 28552 : test_down = test_down + tmp
2069 28552 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2070 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx_im, qs_ot_env(ispin)%matrix_gx_old_im, &
2071 981 : alpha_scalar=beta_pr, beta_scalar=-1.0_dp)
2072 981 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, qs_ot_env(ispin)%matrix_dx_im, tmp)
2073 981 : test_down = test_down + tmp
2074 : END IF
2075 80236 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2076 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx, qs_ot_env(ispin)%rot_mat_gx_old, &
2077 1633 : alpha_scalar=beta_pr, beta_scalar=-1.0_dp)
2078 1633 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_dx, tmp)
2079 1633 : test_down = test_down + 0.5_dp*tmp
2080 1633 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2081 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx_im, &
2082 : qs_ot_env(ispin)%rot_mat_gx_old_im, &
2083 395 : alpha_scalar=beta_pr, beta_scalar=-1.0_dp)
2084 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
2085 395 : qs_ot_env(ispin)%rot_mat_dx_im, tmp)
2086 395 : test_down = test_down + 0.5_dp*tmp
2087 : END IF
2088 : END IF
2089 : END DO
2090 : END IF
2091 23132 : IF (do_ener) THEN
2092 606 : DO ispin = 1, nspin
2093 : qs_ot_env(ispin)%ener_dx = beta_pr*qs_ot_env(ispin)%ener_dx - &
2094 2578 : qs_ot_env(ispin)%ener_gx_old
2095 2578 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_dx)
2096 606 : test_down = test_down + tmp
2097 : END DO
2098 : END IF
2099 23132 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, test_down)
2100 :
2101 23132 : IF (test_down >= 0.0_dp) THEN ! reset to SD
2102 672 : beta_pr = 0.0_dp
2103 672 : IF (do_ks) THEN
2104 1525 : DO ispin = 1, nspin
2105 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx, qs_ot_env(ispin)%matrix_gx_old, &
2106 853 : alpha_scalar=beta_pr, beta_scalar=-1.0_dp)
2107 853 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2108 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx_im, qs_ot_env(ispin)%matrix_gx_old_im, &
2109 13 : alpha_scalar=beta_pr, beta_scalar=-1.0_dp)
2110 : END IF
2111 1525 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2112 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx, &
2113 : qs_ot_env(ispin)%rot_mat_gx_old, &
2114 19 : alpha_scalar=beta_pr, beta_scalar=-1.0_dp)
2115 19 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2116 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx_im, &
2117 : qs_ot_env(ispin)%rot_mat_gx_old_im, &
2118 9 : alpha_scalar=beta_pr, beta_scalar=-1.0_dp)
2119 : END IF
2120 : END IF
2121 : END DO
2122 : END IF
2123 672 : IF (do_ener) THEN
2124 10 : DO ispin = 1, nspin
2125 : qs_ot_env(ispin)%ener_dx = beta_pr*qs_ot_env(ispin)%ener_dx - &
2126 58 : qs_ot_env(ispin)%ener_gx_old
2127 : END DO
2128 : END IF
2129 : END IF
2130 :
2131 : CALL ot_try_mermin_response_direction( &
2132 23132 : qs_ot_env, para_env_inter_kp, baseline_delta, test_down, use_response_candidate)
2133 : ! since we change the direction we have to adjust the gradient
2134 23132 : IF (use_response_candidate) THEN
2135 12 : qs_ot_env(1)%OT_METHOD_FULL = "OT CG-R"
2136 12 : qs_ot_env(1)%gradient = test_down
2137 : ELSE
2138 23120 : qs_ot_env(1)%gradient = beta_pr*qs_ot_env(1)%gradient - qs_ot_env(1)%gnorm
2139 : END IF
2140 23132 : qs_ot_env(1)%gnorm_old = qs_ot_env(1)%gnorm
2141 :
2142 23132 : CALL timestop(handle)
2143 :
2144 23132 : END SUBROUTINE ot_new_cg_direction
2145 :
2146 : ! **************************************************************************************************
2147 : !> \brief Returns the smallest shift y <- y + shift*s that meets relative L-BFGS curvature.
2148 : !> \param sy scalar product s.y
2149 : !> \param ss scalar product s.s
2150 : !> \param yy scalar product y.y
2151 : !> \param curvature_tol requested lower bound for (s.y)/sqrt((s.s)(y.y))
2152 : !> \return non-negative finite damping shift, or zero when no valid shift is needed or available
2153 : ! **************************************************************************************************
2154 342 : PURE FUNCTION lbfgs_curvature_damping_shift(sy, ss, yy, curvature_tol) RESULT(damping_shift)
2155 : REAL(KIND=dp), INTENT(IN) :: sy, ss, yy, curvature_tol
2156 : REAL(KIND=dp) :: damping_shift
2157 :
2158 : REAL(KIND=dp) :: curvature_discriminant, target_sy
2159 :
2160 342 : damping_shift = 0.0_dp
2161 : IF (.NOT. ieee_is_finite(sy) .OR. .NOT. ieee_is_finite(ss) .OR. &
2162 : .NOT. ieee_is_finite(yy) .OR. ss <= 0.0_dp .OR. yy <= 0.0_dp .OR. &
2163 342 : curvature_tol < 0.0_dp .OR. curvature_tol >= 1.0_dp) RETURN
2164 :
2165 342 : curvature_discriminant = MAX(0.0_dp, ss*yy - sy*sy)
2166 : target_sy = curvature_tol/SQRT(MAX(TINY(1.0_dp), 1.0_dp - curvature_tol**2))* &
2167 342 : SQRT(curvature_discriminant)
2168 342 : IF (sy <= target_sy) THEN
2169 4 : target_sy = target_sy*(1.0_dp + SQRT(EPSILON(1.0_dp)))
2170 4 : damping_shift = MAX(0.0_dp, (target_sy - sy)/ss)
2171 4 : IF (.NOT. ieee_is_finite(damping_shift)) damping_shift = 0.0_dp
2172 : END IF
2173 : END FUNCTION lbfgs_curvature_damping_shift
2174 :
2175 : ! **************************************************************************************************
2176 : !> \brief Decides whether an L-BFGS history must be discarded after excessive gradient growth.
2177 : !> \param current_gradient_norm_sq squared norm at the current accepted point
2178 : !> \param previous_gradient_norm_sq squared norm at the preceding accepted point
2179 : !> \return true when both norms are finite and the gradient grew by more than a factor of ten
2180 : ! **************************************************************************************************
2181 346 : PURE FUNCTION lbfgs_history_restart_required(current_gradient_norm_sq, &
2182 : previous_gradient_norm_sq) RESULT(restart_history)
2183 : REAL(KIND=dp), INTENT(IN) :: current_gradient_norm_sq, &
2184 : previous_gradient_norm_sq
2185 : LOGICAL :: restart_history
2186 :
2187 : REAL(KIND=dp), PARAMETER :: gradient_restart_factor = 10.0_dp
2188 :
2189 : restart_history = ieee_is_finite(current_gradient_norm_sq) .AND. &
2190 : ieee_is_finite(previous_gradient_norm_sq) .AND. &
2191 : previous_gradient_norm_sq > TINY(1.0_dp) .AND. &
2192 : current_gradient_norm_sq > gradient_restart_factor**2* &
2193 346 : previous_gradient_norm_sq
2194 346 : END FUNCTION lbfgs_history_restart_required
2195 :
2196 : ! **************************************************************************************************
2197 : !> \brief Decides whether L-BFGS must recover from a collapsed accepted line-search step.
2198 : !> \param accepted_step step selected by the preceding line search
2199 : !> \param reference_step configured initial line-search step
2200 : !> \return true for a finite step smaller than one millionth of a valid reference step
2201 : ! **************************************************************************************************
2202 346 : PURE FUNCTION lbfgs_step_restart_required(accepted_step, reference_step) RESULT(restart_step)
2203 : REAL(KIND=dp), INTENT(IN) :: accepted_step, reference_step
2204 : LOGICAL :: restart_step
2205 :
2206 : REAL(KIND=dp), PARAMETER :: step_restart_factor = 1.0E-6_dp
2207 :
2208 : restart_step = ieee_is_finite(accepted_step) .AND. &
2209 : ieee_is_finite(reference_step) .AND. &
2210 : reference_step > 0.0_dp .AND. &
2211 346 : ABS(accepted_step) < step_restart_factor*reference_step
2212 346 : END FUNCTION lbfgs_step_restart_required
2213 :
2214 : ! **************************************************************************************************
2215 : !> \brief Normalize and damp an occupation-response secant against the conventional H0 direction.
2216 : !>
2217 : !> The response direction is first normalized to the product norm of H0*g. If its directional
2218 : !> derivative is smaller than the Powell bound, it is mixed with H0*g until
2219 : !> g^T*p >= 0.2*g^T*H0*g. Equal-norm normalization and convex mixing bound the target direction,
2220 : !> while the curvature bound keeps the inverse-BFGS completion positive and well conditioned.
2221 : !> \param g_dot_response scalar product of g and the raw response direction
2222 : !> \param response_norm_sq squared product norm of the raw response direction
2223 : !> \param g_dot_h0_g scalar product of g and H0*g
2224 : !> \param h0_g_norm_sq squared product norm of H0*g
2225 : !> \param response_scale scale applied to the raw response direction
2226 : !> \param response_weight convex weight of the normalized response direction
2227 : !> \param valid true if finite positive input permits a response secant
2228 : ! **************************************************************************************************
2229 146 : PURE SUBROUTINE lbfgs_response_secant_parameters( &
2230 : g_dot_response, response_norm_sq, g_dot_h0_g, h0_g_norm_sq, &
2231 : response_scale, response_weight, valid)
2232 : REAL(KIND=dp), INTENT(IN) :: g_dot_response, response_norm_sq, &
2233 : g_dot_h0_g, h0_g_norm_sq
2234 : REAL(KIND=dp), INTENT(OUT) :: response_scale, response_weight
2235 : LOGICAL, INTENT(OUT) :: valid
2236 :
2237 : REAL(KIND=dp), PARAMETER :: powell_fraction = 0.2_dp
2238 :
2239 : REAL(KIND=dp) :: scaled_curvature
2240 :
2241 146 : response_scale = 1.0_dp
2242 146 : response_weight = 0.0_dp
2243 : valid = ieee_is_finite(g_dot_response) .AND. &
2244 : ieee_is_finite(response_norm_sq) .AND. &
2245 : ieee_is_finite(g_dot_h0_g) .AND. &
2246 : ieee_is_finite(h0_g_norm_sq) .AND. &
2247 : g_dot_response > 0.0_dp .AND. &
2248 : response_norm_sq > TINY(1.0_dp) .AND. &
2249 : g_dot_h0_g > 0.0_dp .AND. &
2250 146 : h0_g_norm_sq > TINY(1.0_dp)
2251 146 : IF (.NOT. valid) RETURN
2252 :
2253 144 : response_scale = SQRT(h0_g_norm_sq/response_norm_sq)
2254 144 : scaled_curvature = response_scale*g_dot_response
2255 : IF (.NOT. ieee_is_finite(response_scale) .OR. &
2256 144 : .NOT. ieee_is_finite(scaled_curvature) .OR. scaled_curvature <= 0.0_dp) THEN
2257 0 : response_scale = 1.0_dp
2258 0 : valid = .FALSE.
2259 0 : RETURN
2260 : END IF
2261 :
2262 144 : response_weight = 1.0_dp
2263 144 : IF (scaled_curvature < powell_fraction*g_dot_h0_g) THEN
2264 : response_weight = (1.0_dp - powell_fraction)*g_dot_h0_g/ &
2265 66 : (g_dot_h0_g - scaled_curvature)
2266 : END IF
2267 : END SUBROUTINE lbfgs_response_secant_parameters
2268 :
2269 : ! **************************************************************************************************
2270 : !> \brief Apply the L-BFGS initial inverse Hessian in the OT product space.
2271 : !>
2272 : !> The conventional OT preconditioner defines the base operator H0. If occupation response is
2273 : !> enabled, a norm-bounded Powell-damped response direction p defines a positive inverse-BFGS
2274 : !> secant completion. It maps the current physical gradient g to p while remaining a linear
2275 : !> operator for the arbitrary vector q produced by the first L-BFGS loop:
2276 : !>
2277 : !> H = (I - rho*p*g^T)*H0*(I - rho*g*p^T) + rho*p*p^T,
2278 : !>
2279 : !> where rho = 1/(g^T*p). The m+1 history slot is scratch space for p; callers restore the current
2280 : !> physical gradient there after this routine returns.
2281 : !> \param qs_ot_env OT environments for all local spin/k-point channels
2282 : !> \param gamma scalar H0 scale for orbital and energy variables without an OT preconditioner
2283 : !> \param gamma_rotation H0 scale for rotation variables
2284 : !> \param scratch_index history slot used to store p
2285 : !> \param para_env_inter_kp communicator between distributed k-point groups
2286 : ! **************************************************************************************************
2287 600 : SUBROUTINE ot_apply_lbfgs_initial_inverse( &
2288 : qs_ot_env, gamma, gamma_rotation, scratch_index, para_env_inter_kp)
2289 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
2290 : REAL(KIND=dp), INTENT(IN) :: gamma, gamma_rotation
2291 : INTEGER, INTENT(IN) :: scratch_index
2292 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
2293 :
2294 : INTEGER :: ispin
2295 : LOGICAL :: valid_response
2296 : REAL(KIND=dp) :: coeff_p, coeff_v, g_dot_h0_g, g_dot_h0_q, g_dot_p, h0_g_norm_sq, p_dot_q, &
2297 : p_norm_sq, response_scale, response_weight, rho, tmp
2298 : TYPE(dbcsr_type), POINTER :: scratch_im
2299 :
2300 : ! Apply the base operator to q, which is stored in the *_gx_old work arrays.
2301 1154 : DO ispin = 1, SIZE(qs_ot_env)
2302 : CALL ot_apply_channel_preconditioner(qs_ot_env(ispin), &
2303 : qs_ot_env(ispin)%matrix_gx_old, &
2304 : qs_ot_env(ispin)%matrix_dx, &
2305 : qs_ot_env(ispin)%matrix_gx_old_im, &
2306 694 : qs_ot_env(ispin)%matrix_dx_im, identity_scale=gamma)
2307 694 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2308 566 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx, qs_ot_env(ispin)%rot_mat_gx_old)
2309 566 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_dx, gamma_rotation)
2310 566 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2311 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx_im, &
2312 566 : qs_ot_env(ispin)%rot_mat_gx_old_im)
2313 566 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_dx_im, gamma_rotation)
2314 : END IF
2315 : END IF
2316 1154 : IF (qs_ot_env(1)%settings%do_ener) THEN
2317 2612 : qs_ot_env(ispin)%ener_dx = gamma*qs_ot_env(ispin)%ener_gx_old
2318 : END IF
2319 : END DO
2320 460 : IF (.NOT. qs_ot_env(1)%settings%occupation_preconditioner) RETURN
2321 :
2322 : ! Build the desired occupation-preconditioned product direction p in the scratch slot.
2323 344 : DO ispin = 1, SIZE(qs_ot_env)
2324 204 : CPASSERT(ASSOCIATED(qs_ot_env(ispin)%matrix_preconditioned_gx))
2325 204 : NULLIFY (scratch_im)
2326 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2327 204 : scratch_im => qs_ot_env(ispin)%matrix_h_e_im(scratch_index)%matrix
2328 : END IF
2329 : CALL ot_apply_channel_preconditioner(qs_ot_env(ispin), &
2330 : qs_ot_env(ispin)%matrix_preconditioned_gx, &
2331 : qs_ot_env(ispin)%matrix_h_e(scratch_index)%matrix, &
2332 : qs_ot_env(ispin)%matrix_preconditioned_gx_im, &
2333 204 : scratch_im)
2334 204 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2335 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_e(scratch_index)%matrix, &
2336 204 : qs_ot_env(ispin)%rot_mat_gx)
2337 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_h_e(scratch_index)%matrix, &
2338 204 : gamma_rotation)
2339 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2340 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_e_im(scratch_index)%matrix, &
2341 204 : qs_ot_env(ispin)%rot_mat_gx_im)
2342 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_h_e_im(scratch_index)%matrix, &
2343 204 : gamma_rotation)
2344 : END IF
2345 : END IF
2346 344 : IF (qs_ot_env(1)%settings%do_ener) THEN
2347 204 : CPASSERT(ASSOCIATED(qs_ot_env(ispin)%ener_preconditioned_gx))
2348 : qs_ot_env(ispin)%ener_h_e(scratch_index, :) = &
2349 1140 : qs_ot_env(ispin)%ener_preconditioned_gx
2350 : END IF
2351 : END DO
2352 :
2353 140 : g_dot_h0_q = 0.0_dp
2354 140 : g_dot_p = 0.0_dp
2355 140 : p_dot_q = 0.0_dp
2356 140 : p_norm_sq = 0.0_dp
2357 344 : DO ispin = 1, SIZE(qs_ot_env)
2358 204 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_dx, tmp)
2359 204 : g_dot_h0_q = g_dot_h0_q + tmp
2360 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, &
2361 204 : qs_ot_env(ispin)%matrix_h_e(scratch_index)%matrix, tmp)
2362 204 : g_dot_p = g_dot_p + tmp
2363 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_e(scratch_index)%matrix, &
2364 204 : qs_ot_env(ispin)%matrix_gx_old, tmp)
2365 204 : p_dot_q = p_dot_q + tmp
2366 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_e(scratch_index)%matrix, &
2367 204 : qs_ot_env(ispin)%matrix_h_e(scratch_index)%matrix, tmp)
2368 204 : p_norm_sq = p_norm_sq + tmp
2369 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2370 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, &
2371 204 : qs_ot_env(ispin)%matrix_dx_im, tmp)
2372 204 : g_dot_h0_q = g_dot_h0_q + tmp
2373 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, &
2374 204 : qs_ot_env(ispin)%matrix_h_e_im(scratch_index)%matrix, tmp)
2375 204 : g_dot_p = g_dot_p + tmp
2376 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_e_im(scratch_index)%matrix, &
2377 204 : qs_ot_env(ispin)%matrix_gx_old_im, tmp)
2378 204 : p_dot_q = p_dot_q + tmp
2379 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_e_im(scratch_index)%matrix, &
2380 204 : qs_ot_env(ispin)%matrix_h_e_im(scratch_index)%matrix, tmp)
2381 204 : p_norm_sq = p_norm_sq + tmp
2382 : END IF
2383 204 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2384 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, &
2385 204 : qs_ot_env(ispin)%rot_mat_dx, tmp)
2386 204 : g_dot_h0_q = g_dot_h0_q + 0.5_dp*tmp
2387 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, &
2388 204 : qs_ot_env(ispin)%rot_mat_h_e(scratch_index)%matrix, tmp)
2389 204 : g_dot_p = g_dot_p + 0.5_dp*tmp
2390 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_e(scratch_index)%matrix, &
2391 204 : qs_ot_env(ispin)%rot_mat_gx_old, tmp)
2392 204 : p_dot_q = p_dot_q + 0.5_dp*tmp
2393 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_e(scratch_index)%matrix, &
2394 204 : qs_ot_env(ispin)%rot_mat_h_e(scratch_index)%matrix, tmp)
2395 204 : p_norm_sq = p_norm_sq + 0.5_dp*tmp
2396 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2397 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
2398 204 : qs_ot_env(ispin)%rot_mat_dx_im, tmp)
2399 204 : g_dot_h0_q = g_dot_h0_q + 0.5_dp*tmp
2400 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
2401 204 : qs_ot_env(ispin)%rot_mat_h_e_im(scratch_index)%matrix, tmp)
2402 204 : g_dot_p = g_dot_p + 0.5_dp*tmp
2403 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_e_im(scratch_index)%matrix, &
2404 204 : qs_ot_env(ispin)%rot_mat_gx_old_im, tmp)
2405 204 : p_dot_q = p_dot_q + 0.5_dp*tmp
2406 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_e_im(scratch_index)%matrix, &
2407 204 : qs_ot_env(ispin)%rot_mat_h_e_im(scratch_index)%matrix, tmp)
2408 204 : p_norm_sq = p_norm_sq + 0.5_dp*tmp
2409 : END IF
2410 : END IF
2411 548 : IF (qs_ot_env(1)%settings%do_ener) THEN
2412 : g_dot_h0_q = g_dot_h0_q + &
2413 1140 : DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_dx)
2414 : g_dot_p = g_dot_p + DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, &
2415 1140 : qs_ot_env(ispin)%ener_h_e(scratch_index, :))
2416 : p_dot_q = p_dot_q + DOT_PRODUCT(qs_ot_env(ispin)%ener_h_e(scratch_index, :), &
2417 1140 : qs_ot_env(ispin)%ener_gx_old)
2418 : p_norm_sq = p_norm_sq + DOT_PRODUCT( &
2419 : qs_ot_env(ispin)%ener_h_e(scratch_index, :), &
2420 1140 : qs_ot_env(ispin)%ener_h_e(scratch_index, :))
2421 : END IF
2422 : END DO
2423 140 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, g_dot_h0_q)
2424 140 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, g_dot_p)
2425 140 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, p_dot_q)
2426 140 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, p_norm_sq)
2427 :
2428 : ! Overwrite q with H0*g; q is no longer needed after p^T*q has been formed.
2429 140 : g_dot_h0_g = 0.0_dp
2430 140 : h0_g_norm_sq = 0.0_dp
2431 344 : DO ispin = 1, SIZE(qs_ot_env)
2432 : CALL ot_apply_channel_preconditioner(qs_ot_env(ispin), &
2433 : qs_ot_env(ispin)%matrix_gx, &
2434 : qs_ot_env(ispin)%matrix_gx_old, &
2435 : qs_ot_env(ispin)%matrix_gx_im, &
2436 204 : qs_ot_env(ispin)%matrix_gx_old_im, identity_scale=gamma)
2437 204 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_gx_old, tmp)
2438 204 : g_dot_h0_g = g_dot_h0_g + tmp
2439 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_old, &
2440 204 : qs_ot_env(ispin)%matrix_gx_old, tmp)
2441 204 : h0_g_norm_sq = h0_g_norm_sq + tmp
2442 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2443 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, &
2444 204 : qs_ot_env(ispin)%matrix_gx_old_im, tmp)
2445 204 : g_dot_h0_g = g_dot_h0_g + tmp
2446 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_old_im, &
2447 204 : qs_ot_env(ispin)%matrix_gx_old_im, tmp)
2448 204 : h0_g_norm_sq = h0_g_norm_sq + tmp
2449 : END IF
2450 204 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2451 204 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old, qs_ot_env(ispin)%rot_mat_gx)
2452 204 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_gx_old, gamma_rotation)
2453 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, &
2454 204 : qs_ot_env(ispin)%rot_mat_gx_old, tmp)
2455 204 : g_dot_h0_g = g_dot_h0_g + 0.5_dp*tmp
2456 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_old, &
2457 204 : qs_ot_env(ispin)%rot_mat_gx_old, tmp)
2458 204 : h0_g_norm_sq = h0_g_norm_sq + 0.5_dp*tmp
2459 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2460 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old_im, &
2461 204 : qs_ot_env(ispin)%rot_mat_gx_im)
2462 204 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_gx_old_im, gamma_rotation)
2463 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
2464 204 : qs_ot_env(ispin)%rot_mat_gx_old_im, tmp)
2465 204 : g_dot_h0_g = g_dot_h0_g + 0.5_dp*tmp
2466 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_old_im, &
2467 204 : qs_ot_env(ispin)%rot_mat_gx_old_im, tmp)
2468 204 : h0_g_norm_sq = h0_g_norm_sq + 0.5_dp*tmp
2469 : END IF
2470 : END IF
2471 548 : IF (qs_ot_env(1)%settings%do_ener) THEN
2472 1140 : qs_ot_env(ispin)%ener_gx_old = gamma*qs_ot_env(ispin)%ener_gx
2473 : g_dot_h0_g = g_dot_h0_g + &
2474 : DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, &
2475 1140 : qs_ot_env(ispin)%ener_gx_old)
2476 : h0_g_norm_sq = h0_g_norm_sq + &
2477 : DOT_PRODUCT(qs_ot_env(ispin)%ener_gx_old, &
2478 1140 : qs_ot_env(ispin)%ener_gx_old)
2479 : END IF
2480 : END DO
2481 140 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, g_dot_h0_g)
2482 140 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, h0_g_norm_sq)
2483 :
2484 : CALL lbfgs_response_secant_parameters( &
2485 : g_dot_p, p_norm_sq, g_dot_h0_g, h0_g_norm_sq, &
2486 140 : response_scale, response_weight, valid_response)
2487 140 : IF (.NOT. valid_response) RETURN
2488 :
2489 : ! Replace p by the bounded, damped response target and update its scalar products.
2490 344 : DO ispin = 1, SIZE(qs_ot_env)
2491 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_h_e(scratch_index)%matrix, &
2492 204 : response_weight*response_scale)
2493 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_h_e(scratch_index)%matrix, &
2494 : qs_ot_env(ispin)%matrix_gx_old, &
2495 204 : alpha_scalar=1.0_dp, beta_scalar=1.0_dp - response_weight)
2496 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2497 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_h_e_im(scratch_index)%matrix, &
2498 204 : response_weight*response_scale)
2499 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_h_e_im(scratch_index)%matrix, &
2500 : qs_ot_env(ispin)%matrix_gx_old_im, &
2501 204 : alpha_scalar=1.0_dp, beta_scalar=1.0_dp - response_weight)
2502 : END IF
2503 204 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2504 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_h_e(scratch_index)%matrix, &
2505 204 : response_weight*response_scale)
2506 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_h_e(scratch_index)%matrix, &
2507 : qs_ot_env(ispin)%rot_mat_gx_old, &
2508 204 : alpha_scalar=1.0_dp, beta_scalar=1.0_dp - response_weight)
2509 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2510 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_h_e_im(scratch_index)%matrix, &
2511 204 : response_weight*response_scale)
2512 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_h_e_im(scratch_index)%matrix, &
2513 : qs_ot_env(ispin)%rot_mat_gx_old_im, &
2514 204 : alpha_scalar=1.0_dp, beta_scalar=1.0_dp - response_weight)
2515 : END IF
2516 : END IF
2517 344 : IF (qs_ot_env(1)%settings%do_ener) THEN
2518 : qs_ot_env(ispin)%ener_h_e(scratch_index, :) = &
2519 : response_weight*response_scale*qs_ot_env(ispin)%ener_h_e(scratch_index, :) + &
2520 1140 : (1.0_dp - response_weight)*qs_ot_env(ispin)%ener_gx_old
2521 : END IF
2522 : END DO
2523 : g_dot_p = response_weight*response_scale*g_dot_p + &
2524 140 : (1.0_dp - response_weight)*g_dot_h0_g
2525 : p_dot_q = response_weight*response_scale*p_dot_q + &
2526 140 : (1.0_dp - response_weight)*g_dot_h0_q
2527 :
2528 140 : rho = 1.0_dp/g_dot_p
2529 140 : coeff_p = -rho*g_dot_h0_q + p_dot_q*(rho + rho*rho*g_dot_h0_g)
2530 140 : coeff_v = -rho*p_dot_q
2531 344 : DO ispin = 1, SIZE(qs_ot_env)
2532 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx, &
2533 : qs_ot_env(ispin)%matrix_h_e(scratch_index)%matrix, &
2534 204 : alpha_scalar=1.0_dp, beta_scalar=coeff_p)
2535 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx, qs_ot_env(ispin)%matrix_gx_old, &
2536 204 : alpha_scalar=1.0_dp, beta_scalar=coeff_v)
2537 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2538 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx_im, &
2539 : qs_ot_env(ispin)%matrix_h_e_im(scratch_index)%matrix, &
2540 204 : alpha_scalar=1.0_dp, beta_scalar=coeff_p)
2541 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx_im, &
2542 : qs_ot_env(ispin)%matrix_gx_old_im, &
2543 204 : alpha_scalar=1.0_dp, beta_scalar=coeff_v)
2544 : END IF
2545 204 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2546 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx, &
2547 : qs_ot_env(ispin)%rot_mat_h_e(scratch_index)%matrix, &
2548 204 : alpha_scalar=1.0_dp, beta_scalar=coeff_p)
2549 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx, qs_ot_env(ispin)%rot_mat_gx_old, &
2550 204 : alpha_scalar=1.0_dp, beta_scalar=coeff_v)
2551 204 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2552 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx_im, &
2553 : qs_ot_env(ispin)%rot_mat_h_e_im(scratch_index)%matrix, &
2554 204 : alpha_scalar=1.0_dp, beta_scalar=coeff_p)
2555 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx_im, &
2556 : qs_ot_env(ispin)%rot_mat_gx_old_im, &
2557 204 : alpha_scalar=1.0_dp, beta_scalar=coeff_v)
2558 : END IF
2559 : END IF
2560 344 : IF (qs_ot_env(1)%settings%do_ener) THEN
2561 : qs_ot_env(ispin)%ener_dx = qs_ot_env(ispin)%ener_dx + &
2562 : coeff_p*qs_ot_env(ispin)%ener_h_e(scratch_index, :) + &
2563 1140 : coeff_v*qs_ot_env(ispin)%ener_gx_old
2564 : END IF
2565 : END DO
2566 : END SUBROUTINE ot_apply_lbfgs_initial_inverse
2567 :
2568 : ! **************************************************************************************************
2569 : !> \brief Builds an L-BFGS direction in the fixed OT product chart.
2570 : !>
2571 : !> The selected OT preconditioner is used as the initial inverse Hessian. History slot m+1 stores
2572 : !> the previous accepted point and gradient; slots 1:m store the circular (s,y) secant history.
2573 : !> Orbital rotations are included with the antisymmetric-matrix metric used by the other OT
2574 : !> minimizers. Weak curvature can be regularized along the step, while rejected updates leave
2575 : !> older valid history intact. Non-descent directions and collapsed line-search steps reset the
2576 : !> history; the latter also restore the configured initial step.
2577 : !> \param qs_ot_env OT environments for all local spin/k-point channels
2578 : !> \param para_env_inter_kp communicator between distributed k-point groups
2579 : ! **************************************************************************************************
2580 460 : SUBROUTINE ot_new_lbfgs_direction(qs_ot_env, para_env_inter_kp)
2581 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
2582 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
2583 :
2584 : CHARACTER(len=*), PARAMETER :: routineN = 'ot_new_lbfgs_direction'
2585 : REAL(KIND=dp), PARAMETER :: rotation_scale_max = 1.0E3_dp, &
2586 : rotation_scale_min = 1.0E-3_dp
2587 :
2588 : INTEGER :: handle, i, ispin, j, m_history, newest, &
2589 : nhistory, nspin
2590 460 : INTEGER, ALLOCATABLE, DIMENSION(:) :: history_index
2591 : LOGICAL :: do_ener, preceding_response_candidate, &
2592 : restart_history, restart_step, &
2593 : use_response_candidate
2594 : REAL(KIND=dp) :: beta, current_gradient_norm_sq, curvature_tol, damping_shift, g_dot_z, &
2595 : gamma, gamma_rotation, nvariables, previous_gradient_norm_sq, ss, ss_rotation, stq, sy, &
2596 : sy_rotation, test_down, tmp, yy, yy_newest, yy_rotation
2597 460 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: alpha, rho
2598 :
2599 460 : CALL timeset(routineN, handle)
2600 :
2601 460 : IF (.NOT. qs_ot_env(1)%settings%ks) THEN
2602 0 : CPABORT("MINIMIZER LBFGS currently requires OT orbital variables")
2603 : END IF
2604 460 : nspin = SIZE(qs_ot_env)
2605 460 : do_ener = qs_ot_env(1)%settings%do_ener
2606 460 : m_history = qs_ot_env(1)%settings%diis_m
2607 460 : curvature_tol = qs_ot_env(1)%settings%lbfgs_curvature_tol
2608 460 : restart_history = .FALSE.
2609 460 : restart_step = .FALSE.
2610 460 : preceding_response_candidate = qs_ot_env(1)%response_candidate_pending
2611 :
2612 : ! A local line search can accept a nearly isoenergetic point whose raw gradient is much
2613 : ! larger than at the preceding point. CG naturally resets after such a loss of conjugacy,
2614 : ! whereas retaining an L-BFGS model can amplify it. Discard only the secant model and use
2615 : ! the physical initial inverse Hessian for the current direction. Likewise, restore the
2616 : ! configured initial step if repeated interpolation has collapsed the accepted step by more
2617 : ! than six orders of magnitude without satisfying the SCF convergence criterion.
2618 460 : IF (qs_ot_env(1)%diis_iter > 0) THEN
2619 342 : current_gradient_norm_sq = 0.0_dp
2620 342 : previous_gradient_norm_sq = 0.0_dp
2621 886 : DO ispin = 1, nspin
2622 544 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_gx, tmp)
2623 544 : current_gradient_norm_sq = current_gradient_norm_sq + tmp
2624 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_e(m_history + 1)%matrix, &
2625 544 : qs_ot_env(ispin)%matrix_h_e(m_history + 1)%matrix, tmp)
2626 544 : previous_gradient_norm_sq = previous_gradient_norm_sq + tmp
2627 544 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2628 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, &
2629 544 : qs_ot_env(ispin)%matrix_gx_im, tmp)
2630 544 : current_gradient_norm_sq = current_gradient_norm_sq + tmp
2631 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_e_im(m_history + 1)%matrix, &
2632 544 : qs_ot_env(ispin)%matrix_h_e_im(m_history + 1)%matrix, tmp)
2633 544 : previous_gradient_norm_sq = previous_gradient_norm_sq + tmp
2634 : END IF
2635 544 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2636 504 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_gx, tmp)
2637 504 : current_gradient_norm_sq = current_gradient_norm_sq + 0.5_dp*tmp
2638 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_e(m_history + 1)%matrix, &
2639 504 : qs_ot_env(ispin)%rot_mat_h_e(m_history + 1)%matrix, tmp)
2640 504 : previous_gradient_norm_sq = previous_gradient_norm_sq + 0.5_dp*tmp
2641 504 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2642 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
2643 504 : qs_ot_env(ispin)%rot_mat_gx_im, tmp)
2644 504 : current_gradient_norm_sq = current_gradient_norm_sq + 0.5_dp*tmp
2645 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_e_im(m_history + 1)%matrix, &
2646 504 : qs_ot_env(ispin)%rot_mat_h_e_im(m_history + 1)%matrix, tmp)
2647 504 : previous_gradient_norm_sq = previous_gradient_norm_sq + 0.5_dp*tmp
2648 : END IF
2649 : END IF
2650 1430 : IF (do_ener) THEN
2651 2330 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, qs_ot_env(ispin)%ener_gx)
2652 242 : current_gradient_norm_sq = current_gradient_norm_sq + tmp
2653 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_h_e(m_history + 1, :), &
2654 2330 : qs_ot_env(ispin)%ener_h_e(m_history + 1, :))
2655 242 : previous_gradient_norm_sq = previous_gradient_norm_sq + tmp
2656 : END IF
2657 : END DO
2658 342 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, current_gradient_norm_sq)
2659 342 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, previous_gradient_norm_sq)
2660 : restart_step = lbfgs_step_restart_required(qs_ot_env(1)%ds_min, &
2661 342 : qs_ot_env(1)%settings%ds_min)
2662 : restart_history = lbfgs_history_restart_required(current_gradient_norm_sq, &
2663 : previous_gradient_norm_sq) .OR. &
2664 342 : restart_step .OR. preceding_response_candidate
2665 : IF (restart_history) THEN
2666 6 : qs_ot_env(1)%diis_iter = 1
2667 6 : IF (restart_step) qs_ot_env(1)%ds_min = qs_ot_env(1)%settings%ds_min
2668 6 : qs_ot_env(1)%OT_METHOD_FULL = "OT L-RST"
2669 : END IF
2670 : END IF
2671 :
2672 : ! Form a candidate secant pair in the direction workspaces relative to the preceding
2673 : ! accepted line-search point. Commit it to the circular history only after the curvature
2674 : ! test, so a rejected candidate cannot overwrite the oldest valid pair in a full buffer.
2675 460 : IF (qs_ot_env(1)%diis_iter > 0 .AND. .NOT. restart_history) THEN
2676 336 : j = MOD(qs_ot_env(1)%diis_iter - 1, m_history) + 1
2677 336 : ss = 0.0_dp
2678 336 : sy = 0.0_dp
2679 336 : sy_rotation = 0.0_dp
2680 336 : ss_rotation = 0.0_dp
2681 336 : yy = 0.0_dp
2682 336 : yy_rotation = 0.0_dp
2683 874 : DO ispin = 1, nspin
2684 538 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_dx, qs_ot_env(ispin)%matrix_x)
2685 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx, &
2686 : qs_ot_env(ispin)%matrix_h_x(m_history + 1)%matrix, &
2687 538 : alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
2688 538 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_gx_old, qs_ot_env(ispin)%matrix_gx)
2689 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_gx_old, &
2690 : qs_ot_env(ispin)%matrix_h_e(m_history + 1)%matrix, &
2691 538 : alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
2692 538 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_dx, qs_ot_env(ispin)%matrix_dx, tmp)
2693 538 : ss = ss + tmp
2694 538 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_dx, qs_ot_env(ispin)%matrix_gx_old, tmp)
2695 538 : sy = sy + tmp
2696 538 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_old, qs_ot_env(ispin)%matrix_gx_old, tmp)
2697 538 : yy = yy + tmp
2698 538 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2699 538 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_dx_im, qs_ot_env(ispin)%matrix_x_im)
2700 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx_im, &
2701 : qs_ot_env(ispin)%matrix_h_x_im(m_history + 1)%matrix, &
2702 538 : alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
2703 538 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_gx_old_im, qs_ot_env(ispin)%matrix_gx_im)
2704 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_gx_old_im, &
2705 : qs_ot_env(ispin)%matrix_h_e_im(m_history + 1)%matrix, &
2706 538 : alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
2707 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_dx_im, &
2708 538 : qs_ot_env(ispin)%matrix_dx_im, tmp)
2709 538 : ss = ss + tmp
2710 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_dx_im, &
2711 538 : qs_ot_env(ispin)%matrix_gx_old_im, tmp)
2712 538 : sy = sy + tmp
2713 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_old_im, &
2714 538 : qs_ot_env(ispin)%matrix_gx_old_im, tmp)
2715 538 : yy = yy + tmp
2716 : END IF
2717 538 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2718 498 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx, qs_ot_env(ispin)%rot_mat_x)
2719 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx, &
2720 : qs_ot_env(ispin)%rot_mat_h_x(m_history + 1)%matrix, &
2721 498 : alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
2722 498 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old, qs_ot_env(ispin)%rot_mat_gx)
2723 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_gx_old, &
2724 : qs_ot_env(ispin)%rot_mat_h_e(m_history + 1)%matrix, &
2725 498 : alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
2726 498 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_dx, qs_ot_env(ispin)%rot_mat_dx, tmp)
2727 498 : ss = ss + 0.5_dp*tmp
2728 498 : ss_rotation = ss_rotation + 0.5_dp*tmp
2729 498 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_dx, qs_ot_env(ispin)%rot_mat_gx_old, tmp)
2730 498 : sy = sy + 0.5_dp*tmp
2731 498 : sy_rotation = sy_rotation + 0.5_dp*tmp
2732 498 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_old, qs_ot_env(ispin)%rot_mat_gx_old, tmp)
2733 498 : yy = yy + 0.5_dp*tmp
2734 498 : yy_rotation = yy_rotation + 0.5_dp*tmp
2735 498 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2736 498 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx_im, qs_ot_env(ispin)%rot_mat_x_im)
2737 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx_im, &
2738 : qs_ot_env(ispin)%rot_mat_h_x_im(m_history + 1)%matrix, &
2739 498 : alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
2740 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old_im, &
2741 498 : qs_ot_env(ispin)%rot_mat_gx_im)
2742 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_gx_old_im, &
2743 : qs_ot_env(ispin)%rot_mat_h_e_im(m_history + 1)%matrix, &
2744 498 : alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
2745 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_dx_im, &
2746 498 : qs_ot_env(ispin)%rot_mat_dx_im, tmp)
2747 498 : ss = ss + 0.5_dp*tmp
2748 498 : ss_rotation = ss_rotation + 0.5_dp*tmp
2749 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_dx_im, &
2750 498 : qs_ot_env(ispin)%rot_mat_gx_old_im, tmp)
2751 498 : sy = sy + 0.5_dp*tmp
2752 498 : sy_rotation = sy_rotation + 0.5_dp*tmp
2753 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_old_im, &
2754 498 : qs_ot_env(ispin)%rot_mat_gx_old_im, tmp)
2755 498 : yy = yy + 0.5_dp*tmp
2756 498 : yy_rotation = yy_rotation + 0.5_dp*tmp
2757 : END IF
2758 : END IF
2759 1412 : IF (do_ener) THEN
2760 : qs_ot_env(ispin)%ener_dx = qs_ot_env(ispin)%ener_x - &
2761 2288 : qs_ot_env(ispin)%ener_h_x(m_history + 1, :)
2762 : qs_ot_env(ispin)%ener_gx_old = qs_ot_env(ispin)%ener_gx - &
2763 2288 : qs_ot_env(ispin)%ener_h_e(m_history + 1, :)
2764 2288 : ss = ss + DOT_PRODUCT(qs_ot_env(ispin)%ener_dx, qs_ot_env(ispin)%ener_dx)
2765 2288 : sy = sy + DOT_PRODUCT(qs_ot_env(ispin)%ener_dx, qs_ot_env(ispin)%ener_gx_old)
2766 2288 : yy = yy + DOT_PRODUCT(qs_ot_env(ispin)%ener_gx_old, qs_ot_env(ispin)%ener_gx_old)
2767 : END IF
2768 : END DO
2769 336 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, ss)
2770 336 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, sy)
2771 336 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, yy)
2772 336 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, ss_rotation)
2773 336 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, sy_rotation)
2774 336 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, yy_rotation)
2775 :
2776 : ! Shift y along s just enough to satisfy the requested relative curvature. With the
2777 : ! product-chart metric this preserves orbital-rotation covariance. The target below is
2778 : ! obtained by solving (s.y)^2 >= tol^2 (s.s)(y.y) for y <- y + shift*s.
2779 336 : damping_shift = 0.0_dp
2780 336 : IF (qs_ot_env(1)%settings%lbfgs_damping) THEN
2781 336 : damping_shift = lbfgs_curvature_damping_shift(sy, ss, yy, curvature_tol)
2782 : END IF
2783 336 : IF (damping_shift > 0.0_dp) THEN
2784 0 : DO ispin = 1, nspin
2785 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_gx_old, &
2786 : qs_ot_env(ispin)%matrix_dx, &
2787 0 : alpha_scalar=1.0_dp, beta_scalar=damping_shift)
2788 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2789 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_gx_old_im, &
2790 : qs_ot_env(ispin)%matrix_dx_im, &
2791 0 : alpha_scalar=1.0_dp, beta_scalar=damping_shift)
2792 : END IF
2793 0 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2794 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_gx_old, &
2795 : qs_ot_env(ispin)%rot_mat_dx, &
2796 0 : alpha_scalar=1.0_dp, beta_scalar=damping_shift)
2797 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2798 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_gx_old_im, &
2799 : qs_ot_env(ispin)%rot_mat_dx_im, &
2800 0 : alpha_scalar=1.0_dp, beta_scalar=damping_shift)
2801 : END IF
2802 : END IF
2803 0 : IF (do_ener) THEN
2804 : qs_ot_env(ispin)%ener_gx_old = qs_ot_env(ispin)%ener_gx_old + &
2805 0 : damping_shift*qs_ot_env(ispin)%ener_dx
2806 : END IF
2807 : END DO
2808 0 : yy = yy + 2.0_dp*damping_shift*sy + damping_shift**2*ss
2809 : yy_rotation = yy_rotation + 2.0_dp*damping_shift*sy_rotation + &
2810 0 : damping_shift**2*ss_rotation
2811 0 : sy = sy + damping_shift*ss
2812 0 : sy_rotation = sy_rotation + damping_shift*ss_rotation
2813 : END IF
2814 : IF (ieee_is_finite(sy) .AND. ieee_is_finite(ss) .AND. ieee_is_finite(yy) .AND. &
2815 336 : ss > 0.0_dp .AND. yy > 0.0_dp .AND. sy > curvature_tol*SQRT(ss*yy)) THEN
2816 874 : DO ispin = 1, nspin
2817 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_h_x(j)%matrix, &
2818 538 : qs_ot_env(ispin)%matrix_dx)
2819 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_h_e(j)%matrix, &
2820 538 : qs_ot_env(ispin)%matrix_gx_old)
2821 538 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2822 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_h_x_im(j)%matrix, &
2823 538 : qs_ot_env(ispin)%matrix_dx_im)
2824 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_h_e_im(j)%matrix, &
2825 538 : qs_ot_env(ispin)%matrix_gx_old_im)
2826 : END IF
2827 538 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2828 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_x(j)%matrix, &
2829 498 : qs_ot_env(ispin)%rot_mat_dx)
2830 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_e(j)%matrix, &
2831 498 : qs_ot_env(ispin)%rot_mat_gx_old)
2832 498 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2833 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_x_im(j)%matrix, &
2834 498 : qs_ot_env(ispin)%rot_mat_dx_im)
2835 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_e_im(j)%matrix, &
2836 498 : qs_ot_env(ispin)%rot_mat_gx_old_im)
2837 : END IF
2838 : END IF
2839 874 : IF (do_ener) THEN
2840 2288 : qs_ot_env(ispin)%ener_h_x(j, :) = qs_ot_env(ispin)%ener_dx
2841 2288 : qs_ot_env(ispin)%ener_h_e(j, :) = qs_ot_env(ispin)%ener_gx_old
2842 : END IF
2843 : END DO
2844 336 : qs_ot_env(1)%lbfgs_rho(j) = 1.0_dp/sy
2845 336 : qs_ot_env(1)%lbfgs_yy(j) = yy
2846 336 : qs_ot_env(1)%lbfgs_sy_rotation(j) = sy_rotation
2847 336 : qs_ot_env(1)%lbfgs_yy_rotation(j) = yy_rotation
2848 336 : qs_ot_env(1)%diis_iter = qs_ot_env(1)%diis_iter + 1
2849 : ELSE
2850 : ! Skip this pair. Older pairs remain valid in the fixed OT chart and are retained;
2851 : ! the descent safeguard below still clears all history if their recursion fails.
2852 0 : qs_ot_env(1)%OT_METHOD_FULL = "OT LSKIP"
2853 : END IF
2854 124 : ELSE IF (qs_ot_env(1)%diis_iter <= 0) THEN
2855 118 : qs_ot_env(1)%diis_iter = 1
2856 : END IF
2857 :
2858 1154 : DO ispin = 1, nspin
2859 694 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_gx_old, qs_ot_env(ispin)%matrix_gx)
2860 694 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2861 694 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_gx_old_im, qs_ot_env(ispin)%matrix_gx_im)
2862 : END IF
2863 694 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2864 566 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old, qs_ot_env(ispin)%rot_mat_gx)
2865 566 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2866 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_gx_old_im, &
2867 566 : qs_ot_env(ispin)%rot_mat_gx_im)
2868 : END IF
2869 : END IF
2870 3498 : IF (do_ener) qs_ot_env(ispin)%ener_gx_old = qs_ot_env(ispin)%ener_gx
2871 : END DO
2872 :
2873 460 : nhistory = MIN(qs_ot_env(1)%diis_iter - 1, m_history)
2874 : ALLOCATE (alpha(MAX(1, nhistory)), rho(MAX(1, nhistory)), &
2875 2760 : history_index(MAX(1, nhistory)))
2876 460 : gamma_rotation = 1.0_dp
2877 460 : yy_newest = 0.0_dp
2878 :
2879 : ! First loop, newest to oldest: q <- V^T g.
2880 460 : IF (nhistory > 0) THEN
2881 336 : newest = MOD(qs_ot_env(1)%diis_iter - 2, m_history) + 1
2882 336 : yy_newest = qs_ot_env(1)%lbfgs_yy(newest)
2883 : IF (qs_ot_env(1)%settings%do_rotation .AND. &
2884 : ieee_is_finite(qs_ot_env(1)%lbfgs_sy_rotation(newest)) .AND. &
2885 : ieee_is_finite(qs_ot_env(1)%lbfgs_yy_rotation(newest)) .AND. &
2886 336 : qs_ot_env(1)%lbfgs_sy_rotation(newest) > 0.0_dp .AND. &
2887 : qs_ot_env(1)%lbfgs_yy_rotation(newest) > 0.0_dp) THEN
2888 : gamma_rotation = MAX(rotation_scale_min, MIN(rotation_scale_max, &
2889 : qs_ot_env(1)%lbfgs_sy_rotation(newest)/ &
2890 308 : qs_ot_env(1)%lbfgs_yy_rotation(newest)))
2891 : END IF
2892 1842 : DO i = 1, nhistory
2893 1506 : history_index(i) = MODULO(newest - i, m_history) + 1
2894 1506 : stq = 0.0_dp
2895 3754 : DO ispin = 1, nspin
2896 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_x(history_index(i))%matrix, &
2897 2248 : qs_ot_env(ispin)%matrix_gx_old, tmp)
2898 2248 : stq = stq + tmp
2899 2248 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2900 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_x_im(history_index(i))%matrix, &
2901 2248 : qs_ot_env(ispin)%matrix_gx_old_im, tmp)
2902 2248 : stq = stq + tmp
2903 : END IF
2904 2248 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2905 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_x(history_index(i))%matrix, &
2906 2152 : qs_ot_env(ispin)%rot_mat_gx_old, tmp)
2907 2152 : stq = stq + 0.5_dp*tmp
2908 2152 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2909 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_x_im(history_index(i))%matrix, &
2910 2152 : qs_ot_env(ispin)%rot_mat_gx_old_im, tmp)
2911 2152 : stq = stq + 0.5_dp*tmp
2912 : END IF
2913 : END IF
2914 6002 : IF (do_ener) THEN
2915 : stq = stq + DOT_PRODUCT(qs_ot_env(ispin)%ener_h_x(history_index(i), :), &
2916 10278 : qs_ot_env(ispin)%ener_gx_old)
2917 : END IF
2918 : END DO
2919 1506 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, stq)
2920 1506 : rho(i) = qs_ot_env(1)%lbfgs_rho(history_index(i))
2921 1506 : alpha(i) = rho(i)*stq
2922 4090 : DO ispin = 1, nspin
2923 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_gx_old, &
2924 : qs_ot_env(ispin)%matrix_h_e(history_index(i))%matrix, &
2925 2248 : alpha_scalar=1.0_dp, beta_scalar=-alpha(i))
2926 2248 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2927 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_gx_old_im, &
2928 : qs_ot_env(ispin)%matrix_h_e_im(history_index(i))%matrix, &
2929 2248 : alpha_scalar=1.0_dp, beta_scalar=-alpha(i))
2930 : END IF
2931 2248 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2932 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_gx_old, &
2933 : qs_ot_env(ispin)%rot_mat_h_e(history_index(i))%matrix, &
2934 2152 : alpha_scalar=1.0_dp, beta_scalar=-alpha(i))
2935 2152 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2936 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_gx_old_im, &
2937 : qs_ot_env(ispin)%rot_mat_h_e_im(history_index(i))%matrix, &
2938 2152 : alpha_scalar=1.0_dp, beta_scalar=-alpha(i))
2939 : END IF
2940 : END IF
2941 3754 : IF (do_ener) THEN
2942 : qs_ot_env(ispin)%ener_gx_old = qs_ot_env(ispin)%ener_gx_old - &
2943 10278 : alpha(i)*qs_ot_env(ispin)%ener_h_e(history_index(i), :)
2944 : END IF
2945 : END DO
2946 : END DO
2947 : END IF
2948 :
2949 : ! Apply the conventional initial inverse Hessian and, when requested, its positive linear
2950 : ! occupation-response completion to the arbitrary vector from the first loop.
2951 124 : gamma = 1.0_dp
2952 336 : IF (nhistory > 0 .AND. yy_newest > 0.0_dp) gamma = 1.0_dp/(rho(1)*yy_newest)
2953 : CALL ot_apply_lbfgs_initial_inverse( &
2954 460 : qs_ot_env, gamma, gamma_rotation, m_history + 1, para_env_inter_kp)
2955 :
2956 : ! Real-orbital convergence uses the conventional coupled residual below.
2957 460 : IF (.NOT. qs_ot_env(1)%has_complex_kpoint_state) THEN
2958 : CALL ot_build_history_residual(qs_ot_env, m_history + 1, 1.0_dp, &
2959 0 : para_env_inter_kp, qs_ot_env(1)%gnorm)
2960 0 : IF (.NOT. ieee_is_finite(qs_ot_env(1)%gnorm) .OR. qs_ot_env(1)%gnorm <= 0.0_dp) THEN
2961 : ! Match the identity-metric descent fallback without using the history-based direction.
2962 : CALL ot_build_history_residual_once(qs_ot_env, m_history + 1, 1.0_dp, .FALSE., &
2963 0 : qs_ot_env(1)%gnorm, use_preconditioner=.FALSE.)
2964 0 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, qs_ot_env(1)%gnorm)
2965 : END IF
2966 : END IF
2967 460 : CALL ot_update_delta(qs_ot_env, nvariables, para_env_inter_kp)
2968 :
2969 : ! The m+1 slot was scratch space for the initial inverse and convergence residual. Preserve
2970 : ! the current accepted point and physical gradient there for the next L-BFGS update.
2971 1154 : DO ispin = 1, nspin
2972 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_h_x(m_history + 1)%matrix, &
2973 694 : qs_ot_env(ispin)%matrix_x)
2974 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_h_e(m_history + 1)%matrix, &
2975 694 : qs_ot_env(ispin)%matrix_gx)
2976 694 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2977 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_h_x_im(m_history + 1)%matrix, &
2978 694 : qs_ot_env(ispin)%matrix_x_im)
2979 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_h_e_im(m_history + 1)%matrix, &
2980 694 : qs_ot_env(ispin)%matrix_gx_im)
2981 : END IF
2982 694 : IF (qs_ot_env(1)%settings%do_rotation) THEN
2983 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_x(m_history + 1)%matrix, &
2984 566 : qs_ot_env(ispin)%rot_mat_x)
2985 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_e(m_history + 1)%matrix, &
2986 566 : qs_ot_env(ispin)%rot_mat_gx)
2987 566 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
2988 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_x_im(m_history + 1)%matrix, &
2989 566 : qs_ot_env(ispin)%rot_mat_x_im)
2990 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_e_im(m_history + 1)%matrix, &
2991 566 : qs_ot_env(ispin)%rot_mat_gx_im)
2992 : END IF
2993 : END IF
2994 1154 : IF (do_ener) THEN
2995 2612 : qs_ot_env(ispin)%ener_h_x(m_history + 1, :) = qs_ot_env(ispin)%ener_x
2996 2612 : qs_ot_env(ispin)%ener_h_e(m_history + 1, :) = qs_ot_env(ispin)%ener_gx
2997 : END IF
2998 : END DO
2999 :
3000 : ! Second loop, oldest to newest: z <- V H0 V^T g.
3001 1966 : DO i = nhistory, 1, -1
3002 1506 : beta = 0.0_dp
3003 3754 : DO ispin = 1, nspin
3004 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_e(history_index(i))%matrix, &
3005 2248 : qs_ot_env(ispin)%matrix_dx, tmp)
3006 2248 : beta = beta + tmp
3007 2248 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3008 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_h_e_im(history_index(i))%matrix, &
3009 2248 : qs_ot_env(ispin)%matrix_dx_im, tmp)
3010 2248 : beta = beta + tmp
3011 : END IF
3012 2248 : IF (qs_ot_env(1)%settings%do_rotation) THEN
3013 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_e(history_index(i))%matrix, &
3014 2152 : qs_ot_env(ispin)%rot_mat_dx, tmp)
3015 2152 : beta = beta + 0.5_dp*tmp
3016 2152 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3017 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_h_e_im(history_index(i))%matrix, &
3018 2152 : qs_ot_env(ispin)%rot_mat_dx_im, tmp)
3019 2152 : beta = beta + 0.5_dp*tmp
3020 : END IF
3021 : END IF
3022 6002 : IF (do_ener) THEN
3023 : beta = beta + DOT_PRODUCT(qs_ot_env(ispin)%ener_h_e(history_index(i), :), &
3024 10278 : qs_ot_env(ispin)%ener_dx)
3025 : END IF
3026 : END DO
3027 1506 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, beta)
3028 1506 : beta = rho(i)*beta
3029 4214 : DO ispin = 1, nspin
3030 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx, &
3031 : qs_ot_env(ispin)%matrix_h_x(history_index(i))%matrix, &
3032 2248 : alpha_scalar=1.0_dp, beta_scalar=alpha(i) - beta)
3033 2248 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3034 : CALL dbcsr_add(qs_ot_env(ispin)%matrix_dx_im, &
3035 : qs_ot_env(ispin)%matrix_h_x_im(history_index(i))%matrix, &
3036 2248 : alpha_scalar=1.0_dp, beta_scalar=alpha(i) - beta)
3037 : END IF
3038 2248 : IF (qs_ot_env(1)%settings%do_rotation) THEN
3039 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx, &
3040 : qs_ot_env(ispin)%rot_mat_h_x(history_index(i))%matrix, &
3041 2152 : alpha_scalar=1.0_dp, beta_scalar=alpha(i) - beta)
3042 2152 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3043 : CALL dbcsr_add(qs_ot_env(ispin)%rot_mat_dx_im, &
3044 : qs_ot_env(ispin)%rot_mat_h_x_im(history_index(i))%matrix, &
3045 2152 : alpha_scalar=1.0_dp, beta_scalar=alpha(i) - beta)
3046 : END IF
3047 : END IF
3048 3754 : IF (do_ener) THEN
3049 : qs_ot_env(ispin)%ener_dx = qs_ot_env(ispin)%ener_dx + &
3050 : (alpha(i) - beta)* &
3051 10278 : qs_ot_env(ispin)%ener_h_x(history_index(i), :)
3052 : END IF
3053 : END DO
3054 : END DO
3055 :
3056 460 : g_dot_z = 0.0_dp
3057 1154 : DO ispin = 1, nspin
3058 694 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_dx, tmp)
3059 694 : g_dot_z = g_dot_z + tmp
3060 694 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3061 694 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, qs_ot_env(ispin)%matrix_dx_im, tmp)
3062 694 : g_dot_z = g_dot_z + tmp
3063 : END IF
3064 694 : IF (qs_ot_env(1)%settings%do_rotation) THEN
3065 566 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_dx, tmp)
3066 566 : g_dot_z = g_dot_z + 0.5_dp*tmp
3067 566 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3068 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
3069 566 : qs_ot_env(ispin)%rot_mat_dx_im, tmp)
3070 566 : g_dot_z = g_dot_z + 0.5_dp*tmp
3071 : END IF
3072 : END IF
3073 1848 : IF (do_ener) THEN
3074 : g_dot_z = g_dot_z + DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, &
3075 2612 : qs_ot_env(ispin)%ener_dx)
3076 : END IF
3077 : END DO
3078 460 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, g_dot_z)
3079 :
3080 : ! Preserve a guaranteed descent direction even if the supplied initial preconditioner is
3081 : ! indefinite or numerical noise corrupts the secant recursion.
3082 460 : IF (.NOT. ieee_is_finite(g_dot_z) .OR. g_dot_z <= 0.0_dp) THEN
3083 0 : qs_ot_env(1)%diis_iter = 1
3084 0 : qs_ot_env(1)%OT_METHOD_FULL = "OT L-SD"
3085 0 : DO ispin = 1, nspin
3086 : CALL ot_apply_channel_preconditioner(qs_ot_env(ispin), &
3087 : qs_ot_env(ispin)%matrix_gx, &
3088 : qs_ot_env(ispin)%matrix_dx, &
3089 : qs_ot_env(ispin)%matrix_gx_im, &
3090 0 : qs_ot_env(ispin)%matrix_dx_im)
3091 0 : IF (qs_ot_env(1)%settings%do_rotation) THEN
3092 0 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx, qs_ot_env(ispin)%rot_mat_gx)
3093 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3094 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx_im, &
3095 0 : qs_ot_env(ispin)%rot_mat_gx_im)
3096 : END IF
3097 : END IF
3098 0 : IF (do_ener) qs_ot_env(ispin)%ener_dx = qs_ot_env(ispin)%ener_gx
3099 : END DO
3100 0 : g_dot_z = 0.0_dp
3101 0 : DO ispin = 1, nspin
3102 0 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_dx, tmp)
3103 0 : g_dot_z = g_dot_z + tmp
3104 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3105 0 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, qs_ot_env(ispin)%matrix_dx_im, tmp)
3106 0 : g_dot_z = g_dot_z + tmp
3107 : END IF
3108 0 : IF (qs_ot_env(1)%settings%do_rotation) THEN
3109 0 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_dx, tmp)
3110 0 : g_dot_z = g_dot_z + 0.5_dp*tmp
3111 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3112 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
3113 0 : qs_ot_env(ispin)%rot_mat_dx_im, tmp)
3114 0 : g_dot_z = g_dot_z + 0.5_dp*tmp
3115 : END IF
3116 : END IF
3117 0 : IF (do_ener) THEN
3118 : g_dot_z = g_dot_z + DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, &
3119 0 : qs_ot_env(ispin)%ener_dx)
3120 : END IF
3121 : END DO
3122 0 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, g_dot_z)
3123 0 : IF (.NOT. ieee_is_finite(g_dot_z) .OR. g_dot_z <= 0.0_dp) THEN
3124 0 : g_dot_z = 0.0_dp
3125 0 : DO ispin = 1, nspin
3126 0 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_dx, qs_ot_env(ispin)%matrix_gx)
3127 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3128 0 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_dx_im, qs_ot_env(ispin)%matrix_gx_im)
3129 : END IF
3130 0 : IF (qs_ot_env(ispin)%settings%ot_algorithm == 'TOD') THEN
3131 : ! The P-projected gradient needs an identity-metric projection for this step.
3132 : CALL qs_ot_project_gradient(qs_ot_env(ispin), qs_ot_env(ispin)%matrix_dx, &
3133 0 : qs_ot_env(ispin)%matrix_dx_im, use_preconditioner=.FALSE.)
3134 : END IF
3135 0 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, qs_ot_env(ispin)%matrix_dx, tmp)
3136 0 : g_dot_z = g_dot_z + tmp
3137 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3138 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, &
3139 0 : qs_ot_env(ispin)%matrix_dx_im, tmp)
3140 0 : g_dot_z = g_dot_z + tmp
3141 : END IF
3142 0 : IF (qs_ot_env(1)%settings%do_rotation) THEN
3143 0 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx, qs_ot_env(ispin)%rot_mat_gx)
3144 0 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, qs_ot_env(ispin)%rot_mat_dx, tmp)
3145 0 : g_dot_z = g_dot_z + 0.5_dp*tmp
3146 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3147 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_dx_im, &
3148 0 : qs_ot_env(ispin)%rot_mat_gx_im)
3149 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
3150 0 : qs_ot_env(ispin)%rot_mat_dx_im, tmp)
3151 0 : g_dot_z = g_dot_z + 0.5_dp*tmp
3152 : END IF
3153 : END IF
3154 0 : IF (do_ener) THEN
3155 0 : qs_ot_env(ispin)%ener_dx = qs_ot_env(ispin)%ener_gx
3156 : g_dot_z = g_dot_z + DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, &
3157 0 : qs_ot_env(ispin)%ener_dx)
3158 : END IF
3159 : END DO
3160 0 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, g_dot_z)
3161 : END IF
3162 : END IF
3163 :
3164 1154 : DO ispin = 1, nspin
3165 694 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_dx, -1.0_dp)
3166 694 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3167 694 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_dx_im, -1.0_dp)
3168 : END IF
3169 694 : IF (qs_ot_env(1)%settings%do_rotation) THEN
3170 566 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_dx, -1.0_dp)
3171 566 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3172 566 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_dx_im, -1.0_dp)
3173 : END IF
3174 : END IF
3175 3498 : IF (do_ener) qs_ot_env(ispin)%ener_dx = -qs_ot_env(ispin)%ener_dx
3176 : END DO
3177 460 : qs_ot_env(1)%gnorm = g_dot_z
3178 460 : qs_ot_env(1)%gradient = -g_dot_z
3179 :
3180 460 : IF (nvariables <= 0.0_dp) qs_ot_env(1)%gradient = 0.0_dp
3181 :
3182 460 : test_down = -g_dot_z
3183 : CALL ot_try_mermin_response_direction( &
3184 460 : qs_ot_env, para_env_inter_kp, qs_ot_env(1)%delta, test_down, use_response_candidate)
3185 460 : IF (use_response_candidate) THEN
3186 : ! A sparse finite-response probe changes the inverse model outside the accumulated
3187 : ! L-BFGS secant space. Restart that space while retaining the accepted point used by the
3188 : ! next update; this avoids mixing a calibrated physical candidate with stale curvature.
3189 8 : qs_ot_env(1)%diis_iter = 1
3190 8 : qs_ot_env(1)%OT_METHOD_FULL = "OT L-R"
3191 8 : qs_ot_env(1)%gnorm = -test_down
3192 8 : qs_ot_env(1)%gradient = test_down
3193 : END IF
3194 :
3195 460 : DEALLOCATE (alpha, rho, history_index)
3196 460 : CALL timestop(handle)
3197 :
3198 1380 : END SUBROUTINE ot_new_lbfgs_direction
3199 :
3200 : ! **************************************************************************************************
3201 : !> \brief Build a preconditioned product residual for history-based OT minimizers.
3202 : !> \param qs_ot_env OT environments for all local spin/k-point channels
3203 : !> \param history_index destination slot in the residual history
3204 : !> \param step_scale scale applied after evaluating the descent product
3205 : !> \param para_env_inter_kp communicator between distributed k-point groups
3206 : !> \param gnorm product of the physical Mermin gradient and preconditioned residual
3207 : ! **************************************************************************************************
3208 51328 : SUBROUTINE ot_build_history_residual(qs_ot_env, history_index, step_scale, &
3209 : para_env_inter_kp, gnorm)
3210 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
3211 : INTEGER, INTENT(IN) :: history_index
3212 : REAL(KIND=dp), INTENT(IN) :: step_scale
3213 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
3214 : REAL(KIND=dp), INTENT(OUT) :: gnorm
3215 :
3216 : LOGICAL :: use_occupation_response
3217 : TYPE(cp_logger_type), POINTER :: logger
3218 :
3219 51328 : use_occupation_response = qs_ot_env(1)%settings%occupation_preconditioner
3220 : CALL ot_build_history_residual_once(qs_ot_env, history_index, step_scale, &
3221 51328 : use_occupation_response, gnorm)
3222 51328 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, gnorm)
3223 :
3224 : ! The occupation response is a coupled orbital/energy approximation. Falling back one block
3225 : ! at a time would change its Schur balance, so replace the complete product residual instead.
3226 51328 : IF (use_occupation_response .AND. gnorm <= 0.0_dp) THEN
3227 2 : CALL ot_build_history_residual_once(qs_ot_env, history_index, step_scale, .FALSE., gnorm)
3228 2 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, gnorm)
3229 : END IF
3230 51328 : IF (gnorm < 0.0_dp) THEN
3231 0 : logger => cp_get_default_logger()
3232 0 : WRITE (cp_logger_get_default_unit_nr(logger), *) "WARNING Preconditioner not positive definite !"
3233 : END IF
3234 51328 : END SUBROUTINE ot_build_history_residual
3235 :
3236 : ! **************************************************************************************************
3237 : !> \brief Build one physical or occupation-response product residual.
3238 : !> \param qs_ot_env OT environments for all local spin/k-point channels
3239 : !> \param history_index destination slot in the residual history
3240 : !> \param step_scale scale applied to the stored residual
3241 : !> \param use_occupation_response use the fixed-N occupation-response approximation
3242 : !> \param gnorm product of the physical Mermin gradient and unscaled residual
3243 : !> \param use_preconditioner apply the orbital preconditioner; defaults to true
3244 : ! **************************************************************************************************
3245 51330 : SUBROUTINE ot_build_history_residual_once(qs_ot_env, history_index, step_scale, &
3246 : use_occupation_response, gnorm, use_preconditioner)
3247 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
3248 : INTEGER, INTENT(IN) :: history_index
3249 : REAL(KIND=dp), INTENT(IN) :: step_scale
3250 : LOGICAL, INTENT(IN) :: use_occupation_response
3251 : REAL(KIND=dp), INTENT(OUT) :: gnorm
3252 : LOGICAL, INTENT(IN), OPTIONAL :: use_preconditioner
3253 :
3254 : INTEGER :: ispin
3255 : LOGICAL :: do_ener, do_ks, precondition
3256 : REAL(KIND=dp) :: tmp
3257 : TYPE(dbcsr_type), POINTER :: history_residual_im, orbital_residual, &
3258 : orbital_residual_im
3259 226614 : TYPE(dbcsr_p_type) :: matrix_out(2, SIZE(qs_ot_env))
3260 :
3261 51330 : do_ks = qs_ot_env(1)%settings%ks
3262 51330 : do_ener = qs_ot_env(1)%settings%do_ener
3263 51330 : precondition = .TRUE.
3264 51330 : IF (PRESENT(use_preconditioner)) precondition = use_preconditioner
3265 51330 : gnorm = 0.0_dp
3266 :
3267 51330 : IF (do_ks) THEN
3268 51330 : IF (precondition) THEN
3269 109758 : DO ispin = 1, SIZE(qs_ot_env)
3270 58428 : matrix_out(1, ispin)%matrix => qs_ot_env(ispin)%matrix_h_e(history_index)%matrix
3271 109758 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3272 2752 : matrix_out(2, ispin)%matrix => qs_ot_env(ispin)%matrix_h_e_im(history_index)%matrix
3273 : END IF
3274 : END DO
3275 51330 : CALL ot_precondition_gradients(qs_ot_env, matrix_out, use_occupation_response)
3276 : END IF
3277 109758 : DO ispin = 1, SIZE(qs_ot_env)
3278 58428 : NULLIFY (history_residual_im, orbital_residual_im)
3279 58428 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3280 2752 : history_residual_im => qs_ot_env(ispin)%matrix_h_e_im(history_index)%matrix
3281 : END IF
3282 58428 : IF (use_occupation_response) THEN
3283 148 : CPASSERT(ASSOCIATED(qs_ot_env(ispin)%matrix_preconditioned_gx))
3284 148 : orbital_residual => qs_ot_env(ispin)%matrix_preconditioned_gx
3285 148 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3286 148 : CPASSERT(ASSOCIATED(qs_ot_env(ispin)%matrix_preconditioned_gx_im))
3287 148 : orbital_residual_im => qs_ot_env(ispin)%matrix_preconditioned_gx_im
3288 : END IF
3289 : ELSE
3290 58280 : orbital_residual => qs_ot_env(ispin)%matrix_gx
3291 58280 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3292 2604 : orbital_residual_im => qs_ot_env(ispin)%matrix_gx_im
3293 : END IF
3294 : END IF
3295 :
3296 58428 : IF (.NOT. precondition) THEN
3297 0 : CALL dbcsr_copy(qs_ot_env(ispin)%matrix_h_e(history_index)%matrix, orbital_residual)
3298 0 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3299 0 : CALL dbcsr_copy(history_residual_im, orbital_residual_im)
3300 : END IF
3301 0 : IF (qs_ot_env(ispin)%settings%ot_algorithm == 'TOD') THEN
3302 : CALL qs_ot_project_gradient(qs_ot_env(ispin), &
3303 : qs_ot_env(ispin)%matrix_h_e(history_index)%matrix, &
3304 0 : history_residual_im, use_preconditioner=.FALSE.)
3305 : END IF
3306 : END IF
3307 :
3308 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx, &
3309 58428 : qs_ot_env(ispin)%matrix_h_e(history_index)%matrix, tmp)
3310 58428 : gnorm = gnorm + tmp
3311 58428 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_h_e(history_index)%matrix, step_scale)
3312 58428 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3313 : CALL dbcsr_dot(qs_ot_env(ispin)%matrix_gx_im, &
3314 2752 : qs_ot_env(ispin)%matrix_h_e_im(history_index)%matrix, tmp)
3315 2752 : gnorm = gnorm + tmp
3316 2752 : CALL dbcsr_scale(qs_ot_env(ispin)%matrix_h_e_im(history_index)%matrix, step_scale)
3317 : END IF
3318 :
3319 168186 : IF (qs_ot_env(ispin)%settings%do_rotation) THEN
3320 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_e(history_index)%matrix, &
3321 750 : qs_ot_env(ispin)%rot_mat_gx)
3322 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx, &
3323 750 : qs_ot_env(ispin)%rot_mat_h_e(history_index)%matrix, tmp)
3324 750 : gnorm = gnorm + 0.5_dp*tmp
3325 750 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_h_e(history_index)%matrix, step_scale)
3326 750 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3327 : CALL dbcsr_copy(qs_ot_env(ispin)%rot_mat_h_e_im(history_index)%matrix, &
3328 392 : qs_ot_env(ispin)%rot_mat_gx_im)
3329 : CALL dbcsr_dot(qs_ot_env(ispin)%rot_mat_gx_im, &
3330 392 : qs_ot_env(ispin)%rot_mat_h_e_im(history_index)%matrix, tmp)
3331 392 : gnorm = gnorm + 0.5_dp*tmp
3332 392 : CALL dbcsr_scale(qs_ot_env(ispin)%rot_mat_h_e_im(history_index)%matrix, step_scale)
3333 : END IF
3334 : END IF
3335 : END DO
3336 : END IF
3337 :
3338 51330 : IF (do_ener) THEN
3339 506 : DO ispin = 1, SIZE(qs_ot_env)
3340 328 : IF (use_occupation_response) THEN
3341 148 : CPASSERT(ASSOCIATED(qs_ot_env(ispin)%ener_preconditioned_gx))
3342 : qs_ot_env(ispin)%ener_h_e(history_index, :) = &
3343 1652 : qs_ot_env(ispin)%ener_preconditioned_gx
3344 : ELSE
3345 1424 : qs_ot_env(ispin)%ener_h_e(history_index, :) = qs_ot_env(ispin)%ener_gx
3346 : END IF
3347 : gnorm = gnorm + DOT_PRODUCT(qs_ot_env(ispin)%ener_gx, &
3348 3076 : qs_ot_env(ispin)%ener_h_e(history_index, :))
3349 : qs_ot_env(ispin)%ener_h_e(history_index, :) = &
3350 3254 : step_scale*qs_ot_env(ispin)%ener_h_e(history_index, :)
3351 : END DO
3352 : END IF
3353 51330 : END SUBROUTINE ot_build_history_residual_once
3354 :
3355 : ! **************************************************************************************************
3356 : !> \brief ...
3357 : !> \param qs_ot_env ...
3358 : !> \param para_env_inter_kp communicator between distributed k-point groups
3359 : ! **************************************************************************************************
3360 51136 : SUBROUTINE ot_diis_step(qs_ot_env, para_env_inter_kp)
3361 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
3362 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
3363 :
3364 : CHARACTER(len=*), PARAMETER :: routineN = 'ot_diis_step'
3365 :
3366 : INTEGER :: component, diis_bound, diis_m, handle, i, info, &
3367 : ispin, j, m, nmat, nspin
3368 : LOGICAL :: do_ener, do_ks, do_ot_sd, local_history
3369 : REAL(KIND=dp) :: nvariables, overlap, tmp, tr_xnew_gx, &
3370 : tr_xold_gx
3371 51136 : REAL(dp), ALLOCATABLE :: coefficients(:), dots(:), dots_new(:), weights(:)
3372 51136 : TYPE(dbcsr_p_type), ALLOCATABLE :: positions(:), gradients(:), history(:, :)
3373 : TYPE(cp_logger_type), POINTER :: logger
3374 : TYPE(mp_comm_type) :: group
3375 :
3376 51136 : CALL timeset(routineN, handle)
3377 :
3378 51136 : logger => cp_get_default_logger()
3379 :
3380 51136 : do_ks = qs_ot_env(1)%settings%ks
3381 51136 : do_ener = qs_ot_env(1)%settings%do_ener
3382 51136 : nspin = SIZE(qs_ot_env)
3383 :
3384 51136 : diis_m = qs_ot_env(1)%settings%diis_m
3385 :
3386 51136 : IF (qs_ot_env(1)%diis_iter < diis_m) THEN
3387 30882 : diis_bound = qs_ot_env(1)%diis_iter + 1
3388 : ELSE
3389 20254 : diis_bound = diis_m
3390 : END IF
3391 :
3392 51136 : j = MOD(qs_ot_env(1)%diis_iter, diis_m) + 1 ! index in the circular array
3393 :
3394 : ! Keep component order identical to the scalar product: real, imaginary, then rotation.
3395 51136 : nmat = 0
3396 51136 : local_history = do_ks
3397 51136 : IF (do_ks) THEN
3398 109342 : DO ispin = 1, nspin
3399 58206 : m = 1
3400 58206 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) m = 2
3401 58206 : nmat = nmat + m
3402 58206 : IF (qs_ot_env(ispin)%settings%do_rotation) nmat = nmat + m
3403 : local_history = local_history .AND. qs_ot_env(ispin)%has_complex_kpoint_state .AND. &
3404 165650 : .NOT. qs_ot_env(ispin)%settings%do_rotation .AND. qs_ot_env(ispin)%para_env%num_pe == 1
3405 : END DO
3406 : END IF
3407 0 : ALLOCATE (positions(nmat), gradients(nmat), history(nmat, 2*diis_bound), &
3408 1850628 : weights(nmat), dots(nmat), dots_new(nmat), coefficients(2*diis_bound))
3409 51136 : component = 0
3410 51136 : IF (do_ks) THEN
3411 109342 : DO ispin = 1, nspin
3412 : CALL add_component(qs_ot_env(ispin)%matrix_x, qs_ot_env(ispin)%matrix_gx, &
3413 58206 : qs_ot_env(ispin)%matrix_h_e, qs_ot_env(ispin)%matrix_h_x, 1.0_dp)
3414 58206 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3415 : CALL add_component(qs_ot_env(ispin)%matrix_x_im, qs_ot_env(ispin)%matrix_gx_im, &
3416 2696 : qs_ot_env(ispin)%matrix_h_e_im, qs_ot_env(ispin)%matrix_h_x_im, 1.0_dp)
3417 : END IF
3418 109342 : IF (qs_ot_env(ispin)%settings%do_rotation) THEN
3419 : CALL add_component(qs_ot_env(ispin)%rot_mat_x, qs_ot_env(ispin)%rot_mat_gx, &
3420 694 : qs_ot_env(ispin)%rot_mat_h_e, qs_ot_env(ispin)%rot_mat_h_x, 0.5_dp)
3421 694 : IF (qs_ot_env(ispin)%has_complex_kpoint_state) THEN
3422 : CALL add_component(qs_ot_env(ispin)%rot_mat_x_im, qs_ot_env(ispin)%rot_mat_gx_im, &
3423 336 : qs_ot_env(ispin)%rot_mat_h_e_im, qs_ot_env(ispin)%rot_mat_h_x_im, 0.5_dp)
3424 : END IF
3425 : END IF
3426 : END DO
3427 : END IF
3428 :
3429 : ! copy the position and the error vector in the diis buffers
3430 :
3431 51136 : IF (nmat > 0) CALL dbcsr_get_info(gradients(1)%matrix, group=group)
3432 113068 : DO m = 1, nmat
3433 113068 : CALL dbcsr_copy(history(m, diis_bound + j)%matrix, positions(m)%matrix)
3434 : END DO
3435 51136 : IF (do_ener) THEN
3436 422 : DO ispin = 1, nspin
3437 3002 : qs_ot_env(ispin)%ener_h_x(j, :) = qs_ot_env(ispin)%ener_x(:)
3438 : END DO
3439 : END IF
3440 : CALL ot_build_history_residual(qs_ot_env, j, -qs_ot_env(1)%ds_min, &
3441 51136 : para_env_inter_kp, qs_ot_env(1)%gnorm)
3442 : CALL ot_update_delta(qs_ot_env, nvariables, para_env_inter_kp, &
3443 51136 : residual_product=qs_ot_env(1)%gnorm)
3444 : ! Handling the case of no free variables to optimize
3445 51136 : IF (nvariables > 0.0_dp) THEN
3446 51110 : qs_ot_env(1)%gradient = -qs_ot_env(1)%gnorm
3447 : ELSE
3448 26 : qs_ot_env(1)%gradient = 0.0_dp
3449 : END IF
3450 :
3451 : ! make the diis matrix and solve it
3452 304641 : DO i = 1, diis_bound
3453 : ! Form the DIIS metric from the gradient and stored steps.
3454 253505 : qs_ot_env(1)%ls_diis(i, j) = 0.0_dp
3455 253505 : CALL dbcsr_dot_batch(gradients, history(:, i), dots, comm=group)
3456 556566 : DO m = 1, nmat
3457 556566 : qs_ot_env(1)%ls_diis(i, j) = qs_ot_env(1)%ls_diis(i, j) - qs_ot_env(1)%ds_min*weights(m)*dots(m)
3458 : END DO
3459 253505 : IF (do_ener) THEN
3460 2046 : DO ispin = 1, nspin
3461 14166 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_gx(:), qs_ot_env(ispin)%ener_h_e(i, :))
3462 2046 : qs_ot_env(1)%ls_diis(i, j) = qs_ot_env(1)%ls_diis(i, j) - qs_ot_env(1)%ds_min*tmp
3463 : END DO
3464 : END IF
3465 253505 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, qs_ot_env(1)%ls_diis(i, j))
3466 253505 : qs_ot_env(1)%ls_diis(j, i) = qs_ot_env(1)%ls_diis(i, j)
3467 253505 : qs_ot_env(1)%ls_diis(i, diis_bound + 1) = 1.0_dp
3468 253505 : qs_ot_env(1)%ls_diis(diis_bound + 1, i) = 1.0_dp
3469 304641 : qs_ot_env(1)%c_diis(i) = 0.0_dp
3470 : END DO
3471 51136 : qs_ot_env(1)%ls_diis(diis_bound + 1, diis_bound + 1) = 0.0_dp
3472 51136 : qs_ot_env(1)%c_diis(diis_bound + 1) = 1.0_dp
3473 : ! put in buffer, dgesv destroys
3474 3769172 : qs_ot_env(1)%lss_diis = qs_ot_env(1)%ls_diis
3475 :
3476 : CALL DGESV(diis_bound + 1, 1, qs_ot_env(1)%lss_diis, diis_m + 1, qs_ot_env(1)%ipivot, &
3477 51136 : qs_ot_env(1)%c_diis, diis_m + 1, info)
3478 :
3479 51136 : IF (info /= 0) THEN
3480 8 : do_ot_sd = .TRUE.
3481 8 : WRITE (cp_logger_get_default_unit_nr(logger), *) "Singular DIIS matrix"
3482 : ELSE
3483 51128 : do_ot_sd = .FALSE.
3484 304593 : coefficients(1:diis_bound) = qs_ot_env(1)%c_diis(1:diis_bound)
3485 304593 : coefficients(diis_bound + 1:2*diis_bound) = qs_ot_env(1)%c_diis(1:diis_bound)
3486 51128 : IF (local_history) THEN
3487 772 : CALL dbcsr_sum_many(positions, history, coefficients)
3488 : ELSE
3489 108468 : DO m = 1, nmat
3490 58112 : CALL dbcsr_set(positions(m)%matrix, 0.0_dp)
3491 681054 : DO i = 1, 2*diis_bound
3492 630698 : CALL dbcsr_add(positions(m)%matrix, history(m, i)%matrix, 1.0_dp, coefficients(i))
3493 : END DO
3494 : END DO
3495 : END IF
3496 51128 : IF (do_ener) THEN
3497 422 : DO ispin = 1, nspin
3498 2852 : qs_ot_env(ispin)%ener_x(:) = 0.0_dp
3499 1598 : DO i = 1, diis_bound
3500 : qs_ot_env(ispin)%ener_x(:) = qs_ot_env(ispin)%ener_x(:) &
3501 14438 : + qs_ot_env(1)%c_diis(i)*qs_ot_env(ispin)%ener_h_e(i, :)
3502 : END DO
3503 1748 : DO i = 1, diis_bound
3504 : qs_ot_env(ispin)%ener_x(:) = qs_ot_env(ispin)%ener_x(:) &
3505 14438 : + qs_ot_env(1)%c_diis(i)*qs_ot_env(ispin)%ener_h_x(i, :)
3506 : END DO
3507 : END DO
3508 : END IF
3509 51128 : qs_ot_env(1)%diis_iter = qs_ot_env(1)%diis_iter + 1
3510 51128 : IF (qs_ot_env(1)%settings%safer_diis) THEN
3511 : ! now, final check, is the step in fact in the direction of the -gradient ?
3512 : ! if not we're walking towards a sadle point, and should avoid that
3513 : ! the direction of the step is x_new-x_old
3514 51128 : tr_xold_gx = 0.0_dp
3515 51128 : tr_xnew_gx = 0.0_dp
3516 51128 : CALL dbcsr_dot_batch(history(:, diis_bound + j), gradients, dots, comm=group)
3517 51128 : CALL dbcsr_dot_batch(positions, gradients, dots_new, comm=group)
3518 113036 : DO m = 1, nmat
3519 61908 : tr_xold_gx = tr_xold_gx + weights(m)*dots(m)
3520 113036 : tr_xnew_gx = tr_xnew_gx + weights(m)*dots_new(m)
3521 : END DO
3522 51128 : IF (do_ener) THEN
3523 422 : DO ispin = 1, nspin
3524 2852 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_h_x(j, :), qs_ot_env(ispin)%ener_gx(:))
3525 272 : tr_xold_gx = tr_xold_gx + tmp
3526 2852 : tmp = DOT_PRODUCT(qs_ot_env(ispin)%ener_x(:), qs_ot_env(ispin)%ener_gx(:))
3527 422 : tr_xnew_gx = tr_xnew_gx + tmp
3528 : END DO
3529 : END IF
3530 51128 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, tr_xold_gx)
3531 51128 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, tr_xnew_gx)
3532 51128 : overlap = (tr_xnew_gx - tr_xold_gx)
3533 : ! OK, bad luck, take a SD step along the preconditioned gradient
3534 51128 : IF (overlap > 0.0_dp) THEN
3535 : do_ot_sd = .TRUE.
3536 : END IF
3537 : END IF
3538 : END IF
3539 :
3540 : IF (do_ot_sd) THEN
3541 1118 : qs_ot_env(1)%OT_METHOD_FULL = "OT SD"
3542 2430 : DO m = 1, nmat
3543 1312 : CALL dbcsr_set(positions(m)%matrix, 0.0_dp)
3544 1312 : CALL dbcsr_add(positions(m)%matrix, history(m, j)%matrix, 1.0_dp, 1.0_dp)
3545 2430 : CALL dbcsr_add(positions(m)%matrix, history(m, diis_bound + j)%matrix, 1.0_dp, 1.0_dp)
3546 : END DO
3547 1118 : IF (do_ener) THEN
3548 12 : DO ispin = 1, nspin
3549 184 : qs_ot_env(ispin)%ener_x(:) = 0._dp
3550 184 : qs_ot_env(ispin)%ener_x(:) = qs_ot_env(ispin)%ener_x(:) + qs_ot_env(ispin)%ener_h_e(j, :)
3551 188 : qs_ot_env(ispin)%ener_x(:) = qs_ot_env(ispin)%ener_x(:) + qs_ot_env(ispin)%ener_h_x(j, :)
3552 : END DO
3553 : END IF
3554 : END IF
3555 :
3556 153408 : CALL timestop(handle)
3557 :
3558 : CONTAINS
3559 :
3560 : ! **************************************************************************************************
3561 : !> \brief Collect one DIIS component without copying matrix data.
3562 : !> \param x current position
3563 : !> \param gx current gradient
3564 : !> \param he error history
3565 : !> \param hx position history
3566 : !> \param weight metric weight of this component
3567 : ! **************************************************************************************************
3568 61932 : SUBROUTINE add_component(x, gx, he, hx, weight)
3569 : TYPE(dbcsr_type), POINTER :: x, gx
3570 : TYPE(dbcsr_p_type), INTENT(IN) :: he(:), hx(:)
3571 : REAL(dp), INTENT(IN) :: weight
3572 :
3573 61932 : component = component + 1
3574 61932 : positions(component)%matrix => x
3575 61932 : gradients(component)%matrix => gx
3576 364993 : history(component, 1:diis_bound) = he(1:diis_bound)
3577 364993 : history(component, diis_bound + 1:2*diis_bound) = hx(1:diis_bound)
3578 61932 : weights(component) = weight
3579 61932 : END SUBROUTINE add_component
3580 :
3581 : END SUBROUTINE ot_diis_step
3582 :
3583 : ! **************************************************************************************************
3584 : !> \brief Energy minimizer by Broyden's method
3585 : !> \param qs_ot_env variable to control minimizer behaviour
3586 : !> \param para_env_inter_kp communicator between distributed k-point groups
3587 : !> \author Kurt Baarman (09.2010)
3588 : ! **************************************************************************************************
3589 192 : SUBROUTINE ot_broyden_step(qs_ot_env, para_env_inter_kp)
3590 : TYPE(qs_ot_type), DIMENSION(:), POINTER :: qs_ot_env
3591 : TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_inter_kp
3592 :
3593 : INTEGER, PARAMETER :: broyden_gradient = 4, &
3594 : broyden_position = 1, &
3595 : broyden_random = 3, &
3596 : broyden_residual = 2
3597 : INTEGER :: diis_bound, diis_m, i, j, k, nspin
3598 192 : INTEGER, ALLOCATABLE, DIMENSION(:) :: circ_index
3599 : LOGICAL :: adaptive_sigma, do_ener, do_ks, &
3600 : do_rotation, enable_flip, forget_history
3601 : REAL(KIND=dp) :: beta, eta, gamma, nvariables, omega, &
3602 : sigma, sigma_dec, sigma_min, tmp, tmp2
3603 192 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: f, x
3604 192 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: G, S
3605 192 : eta = qs_ot_env(1)%settings%broyden_eta
3606 192 : omega = qs_ot_env(1)%settings%broyden_omega
3607 192 : sigma_dec = qs_ot_env(1)%settings%broyden_sigma_decrease
3608 192 : sigma_min = qs_ot_env(1)%settings%broyden_sigma_min
3609 192 : forget_history = qs_ot_env(1)%settings%broyden_forget_history
3610 192 : adaptive_sigma = qs_ot_env(1)%settings%broyden_adaptive_sigma
3611 192 : enable_flip = qs_ot_env(1)%settings%broyden_enable_flip
3612 192 : do_ks = qs_ot_env(1)%settings%ks
3613 192 : do_ener = qs_ot_env(1)%settings%do_ener
3614 192 : do_rotation = qs_ot_env(1)%settings%do_rotation
3615 :
3616 192 : beta = qs_ot_env(1)%settings%broyden_beta
3617 192 : gamma = qs_ot_env(1)%settings%broyden_gamma
3618 192 : IF (adaptive_sigma) THEN
3619 192 : IF (qs_ot_env(1)%broyden_adaptive_sigma < 0.0_dp) THEN
3620 6 : sigma = qs_ot_env(1)%settings%broyden_sigma
3621 : ELSE
3622 : sigma = qs_ot_env(1)%broyden_adaptive_sigma
3623 : END IF
3624 : ELSE
3625 0 : sigma = qs_ot_env(1)%settings%broyden_sigma
3626 : END IF
3627 :
3628 192 : IF (.NOT. do_ks) CPABORT("BROYDEN currently requires OT orbital variables")
3629 :
3630 192 : nspin = SIZE(qs_ot_env)
3631 :
3632 192 : diis_m = qs_ot_env(1)%settings%diis_m
3633 :
3634 192 : IF (qs_ot_env(1)%diis_iter < diis_m) THEN
3635 90 : diis_bound = qs_ot_env(1)%diis_iter + 1
3636 : ELSE
3637 102 : diis_bound = diis_m
3638 : END IF
3639 :
3640 : ! We want x:s, f:s and one random vector
3641 192 : k = 2*diis_bound + 1
3642 768 : ALLOCATE (S(k, k))
3643 576 : ALLOCATE (G(k, k))
3644 576 : ALLOCATE (f(k))
3645 384 : ALLOCATE (x(k))
3646 576 : ALLOCATE (circ_index(diis_bound))
3647 192 : G = 0.0_dp
3648 2500 : DO i = 1, k
3649 2500 : G(i, i) = sigma
3650 : END DO
3651 192 : S = 0.0_dp
3652 :
3653 192 : j = MOD(qs_ot_env(1)%diis_iter, diis_m) + 1 ! index in the circular array
3654 :
3655 192 : CALL broyden_copy_current_to_history(j)
3656 : CALL ot_build_history_residual(qs_ot_env, j, -1.0_dp, para_env_inter_kp, &
3657 192 : qs_ot_env(1)%gnorm)
3658 192 : IF (qs_ot_env(1)%settings%occupation_preconditioner) THEN
3659 26 : CALL broyden_bound_occupation_response(j, qs_ot_env(1)%gnorm)
3660 : END IF
3661 :
3662 : CALL ot_update_delta(qs_ot_env, nvariables, para_env_inter_kp, &
3663 192 : residual_product=qs_ot_env(1)%gnorm)
3664 :
3665 : ! Handling the case of no free variables to optimize
3666 192 : IF (nvariables > 0.0_dp) THEN
3667 192 : qs_ot_env(1)%gradient = -qs_ot_env(1)%gnorm
3668 : ELSE
3669 0 : qs_ot_env(1)%gradient = 0.0_dp
3670 : END IF
3671 :
3672 192 : IF (diis_bound == diis_m) THEN
3673 912 : DO i = 1, diis_bound
3674 912 : circ_index(i) = MOD(j + i - 1, diis_m) + 1
3675 : END DO
3676 : ELSE
3677 338 : DO i = 1, diis_bound
3678 338 : circ_index(i) = i
3679 : END DO
3680 : END IF
3681 :
3682 192 : S = 0.0_dp
3683 192 : CALL broyden_randomize_current()
3684 1250 : DO i = 1, diis_bound
3685 : CALL broyden_product_dot(broyden_position, circ_index(i), &
3686 1058 : broyden_position, circ_index(i), S(i, i))
3687 : CALL broyden_product_dot(broyden_residual, circ_index(i), &
3688 : broyden_residual, circ_index(i), &
3689 1058 : S(i + diis_bound, i + diis_bound))
3690 : ! Preserve the established Broyden model, whose auxiliary vector is orthogonal to history.
3691 1058 : S(i, 2*diis_bound + 1) = 0.0_dp
3692 1058 : S(2*diis_bound + 1, i) = 0.0_dp
3693 1058 : S(i + diis_bound, 2*diis_bound + 1) = 0.0_dp
3694 1058 : S(2*diis_bound + 1, i + diis_bound) = 0.0_dp
3695 3874 : DO k = i + 1, diis_bound
3696 : CALL broyden_product_dot(broyden_position, circ_index(i), &
3697 2816 : broyden_position, circ_index(k), S(i, k))
3698 2816 : S(k, i) = S(i, k)
3699 : CALL broyden_product_dot(broyden_residual, circ_index(i), &
3700 : broyden_residual, circ_index(k), &
3701 2816 : S(diis_bound + i, diis_bound + k))
3702 3874 : S(diis_bound + k, diis_bound + i) = S(diis_bound + i, diis_bound + k)
3703 : END DO
3704 7940 : DO k = 1, diis_bound
3705 : CALL broyden_product_dot(broyden_position, circ_index(i), &
3706 : broyden_residual, circ_index(k), &
3707 6690 : S(i, k + diis_bound))
3708 7748 : S(k + diis_bound, i) = S(i, k + diis_bound)
3709 : END DO
3710 : END DO
3711 : CALL broyden_product_dot(broyden_random, 0, broyden_random, 0, &
3712 192 : S(2*diis_bound + 1, 2*diis_bound + 1))
3713 :
3714 : ! normalize
3715 192 : k = 2*diis_bound + 1
3716 192 : tmp = SQRT(S(k, k))
3717 2500 : S(k, :) = S(k, :)/tmp
3718 2500 : S(:, k) = S(:, k)/tmp
3719 :
3720 192 : IF (diis_bound > 1) THEN
3721 176 : tmp2 = 0.0_dp
3722 176 : i = diis_bound
3723 : CALL broyden_product_dot(broyden_position, circ_index(i), &
3724 176 : broyden_residual, circ_index(i), tmp)
3725 176 : tmp2 = tmp2 + tmp
3726 : CALL broyden_product_dot(broyden_position, circ_index(i - 1), &
3727 176 : broyden_residual, circ_index(i), tmp)
3728 176 : tmp2 = tmp2 - tmp
3729 : CALL broyden_product_dot(broyden_position, circ_index(i), &
3730 176 : broyden_residual, circ_index(i - 1), tmp)
3731 176 : tmp2 = tmp2 - tmp
3732 : CALL broyden_product_dot(broyden_position, circ_index(i - 1), &
3733 176 : broyden_residual, circ_index(i - 1), tmp)
3734 176 : tmp2 = tmp2 + tmp
3735 176 : qs_ot_env(1)%c_broy(i - 1) = tmp2
3736 : END IF
3737 :
3738 192 : qs_ot_env(1)%energy_h(j) = qs_ot_env(1)%etotal
3739 :
3740 : ! If we went uphill, do backtracking line search
3741 1250 : i = MINLOC(qs_ot_env(1)%energy_h(1:diis_bound), dim=1)
3742 192 : IF (i /= j) THEN
3743 26 : sigma = sigma_dec*sigma
3744 26 : qs_ot_env(1)%OT_METHOD_FULL = "OT BTRK"
3745 26 : CALL broyden_set_current_zero()
3746 26 : CALL broyden_add_history(broyden_position, i, 1.0_dp, 1.0_dp - gamma)
3747 26 : CALL broyden_add_history(broyden_position, circ_index(diis_bound), 1.0_dp, gamma)
3748 : ELSE
3749 : ! Construct G
3750 876 : DO i = 2, diis_bound
3751 710 : f = 0.0_dp
3752 710 : x = 0.0_dp
3753 : ! f is df_i
3754 710 : x(i) = 1.0_dp
3755 710 : x(i - 1) = -1.0_dp
3756 : ! x is dx_i
3757 710 : f(diis_bound + i) = 1.0_dp
3758 710 : f(diis_bound + i - 1) = -1.0_dp
3759 710 : tmp = 1.0_dp
3760 : ! We want a pos def Hessian
3761 710 : IF (enable_flip) THEN
3762 710 : IF (qs_ot_env(1)%c_broy(i - 1) > 0) THEN
3763 : !qs_ot_env(1)%OT_METHOD_FULL="OT FLIP"
3764 2 : tmp = -1.0_dp
3765 : END IF
3766 : END IF
3767 :
3768 : ! get dx-Gdf
3769 439280 : x(:) = tmp*x - MATMUL(G, f)
3770 : ! dfSdf
3771 : ! we calculate matmul(S, f) twice. They're small...
3772 439280 : tmp = DOT_PRODUCT(f, MATMUL(S, f))
3773 : ! NOTE THAT S IS SYMMETRIC !!!
3774 439280 : f(:) = MATMUL(S, f)/tmp
3775 : ! the spread is an outer vector product
3776 149856 : G(:, :) = G + SPREAD(x, dim=2, ncopies=SIZE(f))*SPREAD(f, dim=1, ncopies=SIZE(x))
3777 : END DO
3778 166 : f = 0.0_dp
3779 166 : f(2*diis_bound) = 1.0_dp
3780 105338 : x(:) = -beta*MATMUL(G, f)
3781 :
3782 : ! OK, add the vectors now, this sums up to the proposed step
3783 166 : CALL broyden_set_current_zero()
3784 1042 : DO i = 1, diis_bound
3785 : CALL broyden_add_history(broyden_residual, circ_index(i), 1.0_dp, &
3786 1042 : -x(i + diis_bound))
3787 : END DO
3788 1042 : DO i = 1, diis_bound
3789 1042 : CALL broyden_add_history(broyden_position, circ_index(i), 1.0_dp, x(i))
3790 : END DO
3791 :
3792 166 : IF (adaptive_sigma) THEN
3793 166 : tmp = new_sigma(G, S, diis_bound)
3794 : !tmp = tmp * qs_ot_env ( 1 ) % settings % broyden_sigma
3795 166 : tmp = tmp*eta
3796 166 : sigma = MIN(omega*sigma, tmp)
3797 : END IF
3798 :
3799 : ! compute the inner product of direction of the step and gradient
3800 166 : CALL broyden_product_dot(broyden_gradient, 0, broyden_random, 0, tmp)
3801 :
3802 : ! If the direction is not a descent direction, reverse it before adding the current point.
3803 166 : IF (tmp >= 0.0_dp) THEN
3804 8 : qs_ot_env(1)%OT_METHOD_FULL = "OT TURN"
3805 : ! A non-descent product step invalidates the secants that couple orbitals,
3806 : ! rotations, and auxiliary energies. Retain the legacy opt-in behavior
3807 : ! for orbital-only OT, but always restart an inconsistent Mermin history.
3808 8 : IF (broyden_history_restart_required(.TRUE., forget_history, do_ener)) THEN
3809 0 : qs_ot_env(1)%diis_iter = 0
3810 : END IF
3811 8 : sigma = sigma*sigma_dec
3812 8 : CALL broyden_add_history(broyden_position, circ_index(diis_bound), -1.0_dp, 1.0_dp)
3813 : ELSE
3814 158 : CALL broyden_add_history(broyden_position, circ_index(diis_bound), 1.0_dp, 1.0_dp)
3815 : END IF
3816 : END IF
3817 :
3818 : ! get rid of S, G, f, x, circ_index for next round
3819 192 : DEALLOCATE (S, G, f, x, circ_index)
3820 :
3821 : ! update for next round
3822 192 : qs_ot_env(1)%diis_iter = qs_ot_env(1)%diis_iter + 1
3823 384 : qs_ot_env(1)%broyden_adaptive_sigma = MAX(sigma, sigma_min)
3824 :
3825 : CONTAINS
3826 :
3827 : ! **************************************************************************************************
3828 : !> \brief Bound the occupation-response direction against the conventional OT H0 direction.
3829 : !> \param history_index response history slot
3830 : !> \param gnorm physical gradient product with the resulting positive direction
3831 : ! **************************************************************************************************
3832 26 : SUBROUTINE broyden_bound_occupation_response(history_index, gnorm)
3833 : INTEGER, INTENT(IN) :: history_index
3834 : REAL(KIND=dp), INTENT(INOUT) :: gnorm
3835 :
3836 : INTEGER :: ichannel
3837 : LOGICAL :: valid_response
3838 : REAL(KIND=dp) :: g_dot_h0_g, h0_g_norm_sq, p_dot_h0, response_delta_norm_sq, &
3839 : response_norm_sq, response_scale, response_weight
3840 :
3841 26 : CALL broyden_build_h0_current()
3842 26 : CALL broyden_product_dot(broyden_gradient, 0, broyden_random, 0, g_dot_h0_g)
3843 26 : CALL broyden_product_dot(broyden_random, 0, broyden_random, 0, h0_g_norm_sq)
3844 : CALL broyden_product_dot(broyden_residual, history_index, &
3845 26 : broyden_residual, history_index, response_norm_sq)
3846 : CALL broyden_product_dot(broyden_residual, history_index, &
3847 26 : broyden_random, 0, p_dot_h0)
3848 26 : p_dot_h0 = -p_dot_h0
3849 26 : response_delta_norm_sq = response_norm_sq + h0_g_norm_sq - 2.0_dp*p_dot_h0
3850 : valid_response = ieee_is_finite(gnorm) .AND. ieee_is_finite(response_norm_sq) .AND. &
3851 : ieee_is_finite(g_dot_h0_g) .AND. ieee_is_finite(h0_g_norm_sq) .AND. &
3852 : ieee_is_finite(response_delta_norm_sq) .AND. gnorm > 0.0_dp .AND. &
3853 : g_dot_h0_g > 0.0_dp .AND. h0_g_norm_sq > TINY(1.0_dp) .AND. &
3854 26 : response_delta_norm_sq >= 0.0_dp
3855 52 : response_scale = 1.0_dp
3856 26 : response_weight = 0.0_dp
3857 : IF (valid_response) THEN
3858 : ! Keep the response correction inside a product-norm trust ball around H0*g.
3859 : response_weight = MIN(1.0_dp, SQRT(h0_g_norm_sq/ &
3860 26 : MAX(response_delta_norm_sq, TINY(1.0_dp))))
3861 : END IF
3862 : ! Broyden secants require a consistent residual map. Use the state-dependent occupation
3863 : ! response only to seed the first direction, then let the history learn from frozen H0*g.
3864 26 : IF (qs_ot_env(1)%diis_iter > 0) response_weight = 0.0_dp
3865 26 : IF (.NOT. valid_response) THEN
3866 0 : response_scale = 0.0_dp
3867 0 : response_weight = 0.0_dp
3868 : END IF
3869 :
3870 78 : DO ichannel = 1, nspin
3871 : CALL dbcsr_scale(qs_ot_env(ichannel)%matrix_h_e(history_index)%matrix, &
3872 52 : response_weight*response_scale)
3873 : CALL dbcsr_add(qs_ot_env(ichannel)%matrix_h_e(history_index)%matrix, &
3874 52 : qs_ot_env(ichannel)%matrix_x, 1.0_dp, -(1.0_dp - response_weight))
3875 52 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
3876 : CALL dbcsr_scale(qs_ot_env(ichannel)%matrix_h_e_im(history_index)%matrix, &
3877 52 : response_weight*response_scale)
3878 : CALL dbcsr_add(qs_ot_env(ichannel)%matrix_h_e_im(history_index)%matrix, &
3879 : qs_ot_env(ichannel)%matrix_x_im, 1.0_dp, &
3880 52 : -(1.0_dp - response_weight))
3881 : END IF
3882 52 : IF (do_rotation) THEN
3883 : CALL dbcsr_scale(qs_ot_env(ichannel)%rot_mat_h_e(history_index)%matrix, &
3884 52 : response_weight*response_scale)
3885 : CALL dbcsr_add(qs_ot_env(ichannel)%rot_mat_h_e(history_index)%matrix, &
3886 : qs_ot_env(ichannel)%rot_mat_x, 1.0_dp, &
3887 52 : -(1.0_dp - response_weight))
3888 52 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
3889 : CALL dbcsr_scale(qs_ot_env(ichannel)%rot_mat_h_e_im(history_index)%matrix, &
3890 52 : response_weight*response_scale)
3891 : CALL dbcsr_add(qs_ot_env(ichannel)%rot_mat_h_e_im(history_index)%matrix, &
3892 : qs_ot_env(ichannel)%rot_mat_x_im, 1.0_dp, &
3893 52 : -(1.0_dp - response_weight))
3894 : END IF
3895 : END IF
3896 78 : IF (do_ener) THEN
3897 : qs_ot_env(ichannel)%ener_h_e(history_index, :) = &
3898 : response_weight*response_scale*qs_ot_env(ichannel)%ener_h_e(history_index, :) - &
3899 208 : (1.0_dp - response_weight)*qs_ot_env(ichannel)%ener_x
3900 : END IF
3901 : END DO
3902 26 : IF (valid_response) THEN
3903 : gnorm = response_weight*response_scale*gnorm + &
3904 26 : (1.0_dp - response_weight)*g_dot_h0_g
3905 : ELSE
3906 0 : gnorm = g_dot_h0_g
3907 : END IF
3908 26 : END SUBROUTINE broyden_bound_occupation_response
3909 :
3910 : ! **************************************************************************************************
3911 : !> \brief Store the conventional positive OT H0 direction in the current product coordinate.
3912 : ! **************************************************************************************************
3913 26 : SUBROUTINE broyden_build_h0_current()
3914 : INTEGER :: ichannel
3915 :
3916 78 : DO ichannel = 1, nspin
3917 : CALL ot_apply_channel_preconditioner(qs_ot_env(ichannel), &
3918 : qs_ot_env(ichannel)%matrix_gx, &
3919 : qs_ot_env(ichannel)%matrix_x, &
3920 : qs_ot_env(ichannel)%matrix_gx_im, &
3921 52 : qs_ot_env(ichannel)%matrix_x_im)
3922 52 : IF (do_rotation) THEN
3923 52 : CALL dbcsr_copy(qs_ot_env(ichannel)%rot_mat_x, qs_ot_env(ichannel)%rot_mat_gx)
3924 52 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
3925 : CALL dbcsr_copy(qs_ot_env(ichannel)%rot_mat_x_im, &
3926 52 : qs_ot_env(ichannel)%rot_mat_gx_im)
3927 : END IF
3928 : END IF
3929 234 : IF (do_ener) qs_ot_env(ichannel)%ener_x = qs_ot_env(ichannel)%ener_gx
3930 : END DO
3931 26 : END SUBROUTINE broyden_build_h0_current
3932 :
3933 : ! **************************************************************************************************
3934 : !> \brief Copy all current product coordinates into a Broyden history slot.
3935 : !> \param history_index destination history slot
3936 : ! **************************************************************************************************
3937 192 : SUBROUTINE broyden_copy_current_to_history(history_index)
3938 : INTEGER, INTENT(IN) :: history_index
3939 :
3940 : INTEGER :: ichannel
3941 :
3942 410 : DO ichannel = 1, nspin
3943 : CALL dbcsr_copy(qs_ot_env(ichannel)%matrix_h_x(history_index)%matrix, &
3944 218 : qs_ot_env(ichannel)%matrix_x)
3945 218 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
3946 : CALL dbcsr_copy(qs_ot_env(ichannel)%matrix_h_x_im(history_index)%matrix, &
3947 52 : qs_ot_env(ichannel)%matrix_x_im)
3948 : END IF
3949 218 : IF (do_rotation) THEN
3950 : CALL dbcsr_copy(qs_ot_env(ichannel)%rot_mat_h_x(history_index)%matrix, &
3951 52 : qs_ot_env(ichannel)%rot_mat_x)
3952 52 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
3953 : CALL dbcsr_copy(qs_ot_env(ichannel)%rot_mat_h_x_im(history_index)%matrix, &
3954 52 : qs_ot_env(ichannel)%rot_mat_x_im)
3955 : END IF
3956 : END IF
3957 410 : IF (do_ener) THEN
3958 208 : qs_ot_env(ichannel)%ener_h_x(history_index, :) = qs_ot_env(ichannel)%ener_x
3959 : END IF
3960 : END DO
3961 192 : END SUBROUTINE broyden_copy_current_to_history
3962 :
3963 : ! **************************************************************************************************
3964 : !> \brief Fill the current product coordinate with a deterministic auxiliary vector.
3965 : ! **************************************************************************************************
3966 192 : SUBROUTINE broyden_randomize_current()
3967 : INTEGER :: ichannel, icoef
3968 :
3969 410 : DO ichannel = 1, nspin
3970 218 : CALL dbcsr_init_random(qs_ot_env(ichannel)%matrix_x)
3971 218 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
3972 52 : CALL dbcsr_init_random(qs_ot_env(ichannel)%matrix_x_im)
3973 : END IF
3974 218 : IF (do_rotation) THEN
3975 52 : CALL dbcsr_init_random(qs_ot_env(ichannel)%rot_mat_x)
3976 52 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
3977 52 : CALL dbcsr_init_random(qs_ot_env(ichannel)%rot_mat_x_im)
3978 : END IF
3979 : END IF
3980 410 : IF (do_ener) THEN
3981 208 : DO icoef = 1, SIZE(qs_ot_env(ichannel)%ener_x)
3982 : qs_ot_env(ichannel)%ener_x(icoef) = SIN(REAL(icoef + &
3983 : 37*qs_ot_env(ichannel)%spin_index + &
3984 208 : 101*qs_ot_env(ichannel)%kpoint_index, KIND=dp))
3985 : END DO
3986 : END IF
3987 : END DO
3988 192 : END SUBROUTINE broyden_randomize_current
3989 :
3990 : ! **************************************************************************************************
3991 : !> \brief Set all current product coordinates to zero.
3992 : ! **************************************************************************************************
3993 192 : SUBROUTINE broyden_set_current_zero()
3994 : INTEGER :: ichannel
3995 :
3996 410 : DO ichannel = 1, nspin
3997 218 : CALL dbcsr_set(qs_ot_env(ichannel)%matrix_x, 0.0_dp)
3998 218 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
3999 52 : CALL dbcsr_set(qs_ot_env(ichannel)%matrix_x_im, 0.0_dp)
4000 : END IF
4001 218 : IF (do_rotation) THEN
4002 52 : CALL dbcsr_set(qs_ot_env(ichannel)%rot_mat_x, 0.0_dp)
4003 52 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
4004 52 : CALL dbcsr_set(qs_ot_env(ichannel)%rot_mat_x_im, 0.0_dp)
4005 : END IF
4006 : END IF
4007 566 : IF (do_ener) qs_ot_env(ichannel)%ener_x = 0.0_dp
4008 : END DO
4009 192 : END SUBROUTINE broyden_set_current_zero
4010 :
4011 : ! **************************************************************************************************
4012 : !> \brief Add one stored product vector to the current coordinate.
4013 : !> \param vector_kind position or residual history
4014 : !> \param history_index source history slot
4015 : !> \param alpha_current scale of the current coordinate
4016 : !> \param beta_history scale of the stored coordinate
4017 : ! **************************************************************************************************
4018 1970 : SUBROUTINE broyden_add_history(vector_kind, history_index, alpha_current, beta_history)
4019 : INTEGER, INTENT(IN) :: vector_kind, history_index
4020 : REAL(KIND=dp), INTENT(IN) :: alpha_current, beta_history
4021 :
4022 : INTEGER :: ichannel
4023 : TYPE(dbcsr_type), POINTER :: source
4024 :
4025 4222 : DO ichannel = 1, nspin
4026 2252 : CALL broyden_matrix_pointer(vector_kind, history_index, ichannel, .FALSE., .FALSE., source)
4027 2252 : CALL dbcsr_add(qs_ot_env(ichannel)%matrix_x, source, alpha_current, beta_history)
4028 2252 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
4029 564 : CALL broyden_matrix_pointer(vector_kind, history_index, ichannel, .TRUE., .FALSE., source)
4030 564 : CALL dbcsr_add(qs_ot_env(ichannel)%matrix_x_im, source, alpha_current, beta_history)
4031 : END IF
4032 2252 : IF (do_rotation) THEN
4033 564 : CALL broyden_matrix_pointer(vector_kind, history_index, ichannel, .FALSE., .TRUE., source)
4034 564 : CALL dbcsr_add(qs_ot_env(ichannel)%rot_mat_x, source, alpha_current, beta_history)
4035 564 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
4036 564 : CALL broyden_matrix_pointer(vector_kind, history_index, ichannel, .TRUE., .TRUE., source)
4037 564 : CALL dbcsr_add(qs_ot_env(ichannel)%rot_mat_x_im, source, alpha_current, beta_history)
4038 : END IF
4039 : END IF
4040 4222 : IF (do_ener) THEN
4041 872 : SELECT CASE (vector_kind)
4042 : CASE (broyden_position)
4043 : qs_ot_env(ichannel)%ener_x = alpha_current*qs_ot_env(ichannel)%ener_x + &
4044 1232 : beta_history*qs_ot_env(ichannel)%ener_h_x(history_index, :)
4045 : CASE (broyden_residual)
4046 : qs_ot_env(ichannel)%ener_x = alpha_current*qs_ot_env(ichannel)%ener_x + &
4047 1024 : beta_history*qs_ot_env(ichannel)%ener_h_e(history_index, :)
4048 : CASE DEFAULT
4049 564 : CPABORT("Invalid Broyden history vector kind")
4050 : END SELECT
4051 : END IF
4052 : END DO
4053 1970 : END SUBROUTINE broyden_add_history
4054 :
4055 : ! **************************************************************************************************
4056 : !> \brief Product-space scalar product including complex, rotation, and energy coordinates.
4057 : !> \param kind_a first vector kind
4058 : !> \param index_a first history index, ignored for current/gradient vectors
4059 : !> \param kind_b second vector kind
4060 : !> \param index_b second history index, ignored for current/gradient vectors
4061 : !> \param value globally reduced scalar product
4062 : ! **************************************************************************************************
4063 15604 : SUBROUTINE broyden_product_dot(kind_a, index_a, kind_b, index_b, value)
4064 : INTEGER, INTENT(IN) :: kind_a, index_a, kind_b, index_b
4065 : REAL(KIND=dp), INTENT(OUT) :: value
4066 :
4067 : INTEGER :: ichannel
4068 : REAL(KIND=dp) :: dot_value
4069 : REAL(KIND=dp), DIMENSION(:), POINTER :: energy_a, energy_b
4070 : TYPE(dbcsr_type), POINTER :: matrix_a, matrix_b
4071 :
4072 15604 : value = 0.0_dp
4073 33124 : DO ichannel = 1, nspin
4074 17520 : CALL broyden_matrix_pointer(kind_a, index_a, ichannel, .FALSE., .FALSE., matrix_a)
4075 17520 : CALL broyden_matrix_pointer(kind_b, index_b, ichannel, .FALSE., .FALSE., matrix_b)
4076 17520 : CALL dbcsr_dot(matrix_a, matrix_b, dot_value)
4077 17520 : value = value + dot_value
4078 17520 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
4079 3832 : CALL broyden_matrix_pointer(kind_a, index_a, ichannel, .TRUE., .FALSE., matrix_a)
4080 3832 : CALL broyden_matrix_pointer(kind_b, index_b, ichannel, .TRUE., .FALSE., matrix_b)
4081 3832 : CALL dbcsr_dot(matrix_a, matrix_b, dot_value)
4082 3832 : value = value + dot_value
4083 : END IF
4084 17520 : IF (do_rotation) THEN
4085 3832 : CALL broyden_matrix_pointer(kind_a, index_a, ichannel, .FALSE., .TRUE., matrix_a)
4086 3832 : CALL broyden_matrix_pointer(kind_b, index_b, ichannel, .FALSE., .TRUE., matrix_b)
4087 3832 : CALL dbcsr_dot(matrix_a, matrix_b, dot_value)
4088 3832 : value = value + 0.5_dp*dot_value
4089 3832 : IF (qs_ot_env(ichannel)%has_complex_kpoint_state) THEN
4090 3832 : CALL broyden_matrix_pointer(kind_a, index_a, ichannel, .TRUE., .TRUE., matrix_a)
4091 3832 : CALL broyden_matrix_pointer(kind_b, index_b, ichannel, .TRUE., .TRUE., matrix_b)
4092 3832 : CALL dbcsr_dot(matrix_a, matrix_b, dot_value)
4093 3832 : value = value + 0.5_dp*dot_value
4094 : END IF
4095 : END IF
4096 50644 : IF (do_ener) THEN
4097 3832 : CALL broyden_energy_pointer(kind_a, index_a, ichannel, energy_a)
4098 3832 : CALL broyden_energy_pointer(kind_b, index_b, ichannel, energy_b)
4099 15328 : value = value + DOT_PRODUCT(energy_a, energy_b)
4100 : END IF
4101 : END DO
4102 15604 : CALL ot_mini_sum_kpoint_scalar(para_env_inter_kp, value)
4103 15604 : END SUBROUTINE broyden_product_dot
4104 :
4105 : ! **************************************************************************************************
4106 : !> \brief Select one orbital or rotation matrix from the product-vector storage.
4107 : !> \param vector_kind vector kind
4108 : !> \param history_index history slot
4109 : !> \param channel local spin/k-point channel
4110 : !> \param imaginary select imaginary component
4111 : !> \param rotation select rotation instead of orbital component
4112 : !> \param matrix selected matrix
4113 : ! **************************************************************************************************
4114 61976 : SUBROUTINE broyden_matrix_pointer(vector_kind, history_index, channel, imaginary, rotation, matrix)
4115 : INTEGER, INTENT(IN) :: vector_kind, history_index, channel
4116 : LOGICAL, INTENT(IN) :: imaginary, rotation
4117 : TYPE(dbcsr_type), POINTER :: matrix
4118 :
4119 61976 : IF (imaginary) THEN
4120 16456 : CPASSERT(qs_ot_env(channel)%has_complex_kpoint_state)
4121 : END IF
4122 91610 : SELECT CASE (vector_kind)
4123 : CASE (broyden_position)
4124 29634 : CPASSERT(history_index > 0)
4125 29634 : IF (rotation) THEN
4126 7656 : IF (imaginary) THEN
4127 3828 : matrix => qs_ot_env(channel)%rot_mat_h_x_im(history_index)%matrix
4128 : ELSE
4129 3828 : matrix => qs_ot_env(channel)%rot_mat_h_x(history_index)%matrix
4130 : END IF
4131 : ELSE
4132 21978 : IF (imaginary) THEN
4133 3828 : matrix => qs_ot_env(channel)%matrix_h_x_im(history_index)%matrix
4134 : ELSE
4135 18150 : matrix => qs_ot_env(channel)%matrix_h_x(history_index)%matrix
4136 : END IF
4137 : END IF
4138 : CASE (broyden_residual)
4139 29858 : CPASSERT(history_index > 0)
4140 29858 : IF (rotation) THEN
4141 7864 : IF (imaginary) THEN
4142 3932 : matrix => qs_ot_env(channel)%rot_mat_h_e_im(history_index)%matrix
4143 : ELSE
4144 3932 : matrix => qs_ot_env(channel)%rot_mat_h_e(history_index)%matrix
4145 : END IF
4146 : ELSE
4147 21994 : IF (imaginary) THEN
4148 3932 : matrix => qs_ot_env(channel)%matrix_h_e_im(history_index)%matrix
4149 : ELSE
4150 18062 : matrix => qs_ot_env(channel)%matrix_h_e(history_index)%matrix
4151 : END IF
4152 : END IF
4153 : CASE (broyden_random)
4154 1928 : IF (rotation) THEN
4155 728 : IF (imaginary) THEN
4156 364 : matrix => qs_ot_env(channel)%rot_mat_x_im
4157 : ELSE
4158 364 : matrix => qs_ot_env(channel)%rot_mat_x
4159 : END IF
4160 : ELSE
4161 1200 : IF (imaginary) THEN
4162 364 : matrix => qs_ot_env(channel)%matrix_x_im
4163 : ELSE
4164 836 : matrix => qs_ot_env(channel)%matrix_x
4165 : END IF
4166 : END IF
4167 : CASE (broyden_gradient)
4168 556 : IF (rotation) THEN
4169 208 : IF (imaginary) THEN
4170 104 : matrix => qs_ot_env(channel)%rot_mat_gx_im
4171 : ELSE
4172 104 : matrix => qs_ot_env(channel)%rot_mat_gx
4173 : END IF
4174 : ELSE
4175 348 : IF (imaginary) THEN
4176 104 : matrix => qs_ot_env(channel)%matrix_gx_im
4177 : ELSE
4178 244 : matrix => qs_ot_env(channel)%matrix_gx
4179 : END IF
4180 : END IF
4181 : CASE DEFAULT
4182 61976 : CPABORT("Invalid Broyden product vector kind")
4183 : END SELECT
4184 61976 : END SUBROUTINE broyden_matrix_pointer
4185 :
4186 : ! **************************************************************************************************
4187 : !> \brief Select one energy-variable vector from the product-vector storage.
4188 : !> \param vector_kind vector kind
4189 : !> \param history_index history slot
4190 : !> \param channel local spin/k-point channel
4191 : !> \param energy selected energy vector
4192 : ! **************************************************************************************************
4193 7664 : SUBROUTINE broyden_energy_pointer(vector_kind, history_index, channel, energy)
4194 : INTEGER, INTENT(IN) :: vector_kind, history_index, channel
4195 : REAL(KIND=dp), DIMENSION(:), POINTER :: energy
4196 :
4197 11184 : SELECT CASE (vector_kind)
4198 : CASE (broyden_position)
4199 3520 : CPASSERT(history_index > 0)
4200 3520 : energy => qs_ot_env(channel)%ener_h_x(history_index, :)
4201 : CASE (broyden_residual)
4202 3676 : CPASSERT(history_index > 0)
4203 3676 : energy => qs_ot_env(channel)%ener_h_e(history_index, :)
4204 : CASE (broyden_random)
4205 364 : energy => qs_ot_env(channel)%ener_x
4206 : CASE (broyden_gradient)
4207 104 : energy => qs_ot_env(channel)%ener_gx
4208 : CASE DEFAULT
4209 7664 : CPABORT("Invalid Broyden energy vector kind")
4210 : END SELECT
4211 7664 : END SUBROUTINE broyden_energy_pointer
4212 :
4213 : END SUBROUTINE ot_broyden_step
4214 :
4215 : ! **************************************************************************************************
4216 : !> \brief ...
4217 : !> \param G ...
4218 : !> \param S ...
4219 : !> \param n ...
4220 : !> \return ...
4221 : ! **************************************************************************************************
4222 166 : FUNCTION new_sigma(G, S, n) RESULT(sigma)
4223 : !
4224 : ! Calculate new sigma from eigenvalues of full size G by Arnoldi.
4225 : !
4226 : ! **************************************************************************************************
4227 :
4228 : REAL(KIND=dp), DIMENSION(:, :) :: G, S
4229 : INTEGER :: n
4230 : REAL(KIND=dp) :: sigma
4231 :
4232 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigv
4233 166 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: H
4234 :
4235 664 : ALLOCATE (H(n, n))
4236 166 : CALL hess_G(G, S, H, n)
4237 498 : ALLOCATE (eigv(n))
4238 166 : CALL diamat_all(H(1:n, 1:n), eigv)
4239 :
4240 : SELECT CASE (1)
4241 : CASE (1)
4242 : ! This estimator seems to work well. No theory.
4243 2064 : sigma = SUM(ABS(eigv**2))/SUM(ABS(eigv))
4244 : CASE (2)
4245 : ! Estimator based on Frobenius norm minimizer
4246 : sigma = SUM(ABS(eigv))/MAX(1, SIZE(eigv))
4247 : CASE (3)
4248 : ! Estimator based on induced 2-norm
4249 : sigma = (MAXVAL(ABS(eigv)) + MINVAL(ABS(eigv)))*0.5_dp
4250 : END SELECT
4251 :
4252 166 : DEALLOCATE (H, eigv)
4253 166 : END FUNCTION new_sigma
4254 :
4255 : ! **************************************************************************************************
4256 : !> \brief ...
4257 : !> \param G ...
4258 : !> \param S ...
4259 : !> \param H ...
4260 : !> \param n ...
4261 : ! **************************************************************************************************
4262 166 : SUBROUTINE hess_G(G, S, H, n)
4263 : !
4264 : ! Make a hessenberg out of G into H. Cf Arnoldi.
4265 : ! Inner product is weighted by S.
4266 : ! Possible lucky breakdown at n.
4267 : !
4268 : ! **************************************************************************************************
4269 : REAL(KIND=dp), DIMENSION(:, :) :: G, S, H
4270 : INTEGER :: n
4271 :
4272 : INTEGER :: i, j, k
4273 : REAL(KIND=dp) :: tmp
4274 166 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: v
4275 166 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: Q
4276 :
4277 166 : i = SIZE(G, 1)
4278 166 : k = SIZE(H, 1)
4279 664 : ALLOCATE (Q(i, k))
4280 498 : ALLOCATE (v(i))
4281 6458 : H = 0.0_dp
4282 166 : Q = 0.0_dp
4283 :
4284 2084 : Q(:, 1) = 1.0_dp
4285 29668 : tmp = SQRT(DOT_PRODUCT(Q(:, 1), MATMUL(S, Q(:, 1))))
4286 12750 : Q(:, :) = Q(:, :)/tmp
4287 :
4288 1028 : DO i = 1, k
4289 174698 : v(:) = MATMUL(G, Q(:, i))
4290 3954 : DO j = 1, i
4291 715800 : H(j, i) = DOT_PRODUCT(Q(:, j), MATMUL(S, v))
4292 47142 : v(:) = v - H(j, i)*Q(:, j)
4293 : END DO
4294 1028 : IF (i < k) THEN
4295 159352 : tmp = DOT_PRODUCT(v, MATMUL(S, v))
4296 704 : IF (tmp <= 0.0_dp) THEN
4297 0 : n = i
4298 0 : EXIT
4299 : END IF
4300 704 : tmp = SQRT(tmp)
4301 : ! Lucky breakdown
4302 704 : IF (ABS(tmp) < 1e-9_dp) THEN
4303 4 : n = i
4304 4 : EXIT
4305 : END IF
4306 700 : H(i + 1, i) = tmp
4307 10340 : Q(:, i + 1) = v/H(i + 1, i)
4308 : END IF
4309 : END DO
4310 :
4311 166 : DEALLOCATE (Q, v)
4312 166 : END SUBROUTINE hess_G
4313 :
4314 6088 : END MODULE qs_ot_minimizer
|