Octopus
eigen_evolution.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch
2!! Copyright (C) 2025 S. Ohlmann
3!!
4!! This program is free software; you can redistribute it and/or modify
5!! it under the terms of the GNU General Public License as published by
6!! the Free Software Foundation; either version 2, or (at your option)
7!! any later version.
8!!
9!! This program is distributed in the hope that it will be useful,
10!! but WITHOUT ANY WARRANTY; without even the implied warranty of
11!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12!! GNU General Public License for more details.
13!!
14!! You should have received a copy of the GNU General Public License
15!! along with this program; if not, write to the Free Software
16!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17!! 02110-1301, USA.
18!!
19
20#include "global.h"
21
23 use debug_oct_m
25 use global_oct_m
30 use mesh_oct_m
32 use mpi_oct_m
36 use space_oct_m
38 use types_oct_m
40
41 implicit none
42
43 private
44 public :: &
46
47 integer, public, parameter :: &
48 IT_FORWARD_EULER = 1, &
49 it_expmid = 2
50
51contains
52
54 subroutine eigensolver_evolution(namespace, mesh, st, hm, space, ext_partners, te, iter, niter, converged, &
55 tau0, tau, time, propagator, variable_timestep, vks_old, normalization_energies, normalization_energies_prev, residual)
56 type(namespace_t), intent(in) :: namespace
57 type(mesh_t), target, intent(in) :: mesh
58 type(states_elec_t), target, intent(inout) :: st
59 type(hamiltonian_elec_t), target, intent(inout) :: hm
60 class(space_t), intent(in) :: space
61 type(partner_list_t), intent(in) :: ext_partners
62 type(exponential_t), intent(inout) :: te
63 integer, intent(in) :: iter
64 integer, intent(inout) :: niter
65 integer, intent(inout) :: converged(:)
66 real(real64), intent(inout) :: tau0
67 real(real64), intent(inout) :: tau
68 real(real64), intent(inout) :: time
69 integer, intent(in) :: propagator
70 logical, intent(in) :: variable_timestep
71 type(potential_interpolation_t), intent(inout) :: vks_old
72 real(real64), intent(inout) :: normalization_energies(:, :)
73 real(real64), intent(inout) :: normalization_energies_prev(:, :)
74 real(real64), intent(inout) :: residual(:, :)
75
76 real(real64) :: delta_normalization, delta_expectation, delta_residual
77 real(real64) :: weights(1:st%nst, 1:st%nik)
78 real(real64), parameter :: gam = 1.0_real64
79 real(real64), parameter :: safety = 1.0e-2_real64
80 integer :: ist, ik
81
82 push_sub(eigensolver_evolution)
83
84 ! Warning: it seems that the algorithm is improved if some extra states are added -- states
85 ! whose convergence should not be checked.
86
87 write(message(1), "(A, I5, A, F10.4, A, F10.4)") "Evolution eigensolver: starting iteration ", iter, &
88 " at imaginary time ", time, " with timestep ", tau
89 call messages_info(1)
90
91 if (variable_timestep .and. iter > 2) then
92 do ik = 1, st%nik
93 do ist = 1, st%nst
94 weights(ist, ik) = st%kweights(ik)*st%occ(ist, ik)
95 end do
96 end do
97 ! The variable timestep method is implemented as described in
98 ! Aichinger, M. & Krotscheck, E., Computational Materials Science 34, 188–212 (2005), 10.1016/j.commatsci.2004.11.002
99 ! section 2.5
100 ! eq. 24
101 delta_normalization = norm2(weights*(normalization_energies-normalization_energies_prev))/sqrt(st%qtot)
102 ! eq. 25
103 delta_expectation = norm2(weights*(normalization_energies-st%eigenval))/sqrt(st%qtot)
104 ! also take the residual into account; not in the paper
105 delta_residual = norm2(weights*residual)/sqrt(st%qtot)
106
107 write(message(1), "(A, ES15.2)") "Normalization energy error: ", delta_normalization
108 write(message(2), "(A, ES15.2)") "Expectation energy error: ", delta_expectation
109 write(message(3), "(A, ES15.2)") "Residual: ", delta_residual
110 call messages_info(3, debug_only=.true.)
111
112 ! reduce timestep if normalization energy changes less after one step compared to the
113 ! expectation energy; moreover, it is not allowed to be too small compared to the residual
114 ! to avoid getting "convergence" due to very small timesteps, but with large residuals
115 ! the first condition is eq. 26 in the paper, the second is added by us
116 ! Moreover, let the timestep never be less than the initial timestep divided by 20
117 if (delta_normalization < gam*delta_expectation .and. delta_normalization > delta_residual * safety &
118 .and. tau/m_two >= tau0/20._real64) then
119 tau = tau/m_two
120 write(message(1), "(A, F10.4)") "Evolution eigensolver: reducing timestep to ", tau
121 call messages_info(1)
122 end if
123 end if
124
125 if (variable_timestep) then
126 normalization_energies_prev = normalization_energies
127 end if
128
129 select case(propagator)
130 case(it_forward_euler)
131 call exponential_imaginary_time(namespace, mesh, st, hm, te, converged, tau, normalization_energies)
132 case(it_expmid)
133 call hm%ks_pot%interpolation_new(vks_old, time+tau, tau)
134 call hm%ks_pot%interpolate_potentials(vks_old, 3, time+tau, tau, time + tau/m_two)
135 call hm%update(mesh, namespace, space, ext_partners, time=time+tau/m_two)
136 call exponential_imaginary_time(namespace, mesh, st, hm, te, converged, tau, normalization_energies)
137 case default
138 message(1) = "Unknown propagator for imaginary-time evolution."
139 call messages_fatal(1)
140 end select
141
142
143 ! we cannot estimate the number of matrix-vector products because it depends on the algorithm
144 niter = 0
145
146 time = time + tau
148 pop_sub(eigensolver_evolution)
149 end subroutine eigensolver_evolution
150
151 subroutine exponential_imaginary_time(namespace, mesh, st, hm, te, converged, tau, normalization_energies)
152 type(namespace_t), intent(in) :: namespace
153 type(mesh_t), target, intent(in) :: mesh
154 type(states_elec_t), target, intent(inout) :: st
155 type(hamiltonian_elec_t), target, intent(inout) :: hm
156 type(exponential_t), intent(inout) :: te
157 integer, intent(inout) :: converged(:)
158 real(real64), intent(in) :: tau
159 real(real64), intent(inout) :: normalization_energies(:, :)
160
161 integer :: ib, convb, ik
162 type(wfs_elec_t), pointer :: zpsib
163 complex(real64), allocatable :: dot(:, :)
164
166
167 safe_allocate(dot(1:st%nst, 1:st%nik))
168 dot = m_zero
169
170 do ik = st%d%kpt%start, st%d%kpt%end
171 convb = get_first_unconverged_batch(st, hm, ik, converged)
172
173 do ib = convb + 1, st%group%block_end
174 if (st%group%psib(ib, ik)%type() == type_float) then
175 ! we need to copy to a complex batch for real states to apply the exponential
176 safe_allocate(zpsib)
177 call st%group%psib(ib, ik)%copy_to(zpsib, copy_data=.true., dest_type=type_cmplx)
178 else
179 zpsib => st%group%psib(ib, ik)
180 end if
181
182 ! simply apply the exponential here
183 ! normalization is done in the subspace diagonalization call
184 call hm%phase%set_phase_corr(mesh, zpsib)
185 call te%apply_batch(namespace, mesh, hm, zpsib, tau, imag_time=.true.)
186 call hm%phase%unset_phase_corr(mesh, zpsib)
187
188 ! estimate normalization energy for variable timesteps
189 call zmesh_batch_dotp_vector(mesh, zpsib, zpsib, &
190 dot(states_elec_block_min(st, ib):states_elec_block_max(st, ib), ik), reduce=.false.)
191
192 if (st%group%psib(ib, ik)%type() == type_float) then
193 ! we need to copy back the data for real states
194 call zpsib%copy_data_to(mesh%np, st%group%psib(ib, ik))
195 call zpsib%end()
196 safe_deallocate_p(zpsib)
197 else
198 nullify(zpsib)
199 end if
200 end do
201 end do
202
203 call st%dom_st_kpt_mpi_grp%allreduce_inplace(dot, st%nst*st%nik, mpi_double_complex, mpi_sum)
204 ! estimate energies from change in normalization
205 normalization_energies = -log(abs(dot))/(m_two*tau)
206
207 safe_deallocate_a(dot)
208
210 end subroutine exponential_imaginary_time
211
212 function get_first_unconverged_batch(st, hm, ik, converged) result(convb)
213 type(states_elec_t), intent(in) :: st
214 type(hamiltonian_elec_t), intent(in) :: hm
215 integer, intent(in) :: ik
216 integer, intent(in) :: converged(:)
217 integer :: convb
218
219 integer :: conv, ib
220
222
223 convb = st%group%block_start - 1
224 ! In case of independent particles, the Hamiltonian does not depends upon the density and we can freeze
225 ! the lowest converged states
226 if (hm%theory_level == independent_particles) then
227 conv = 0
228 do ib = st%group%block_start, st%group%block_end
229 if (conv + st%group%psib(ib, ik)%nst <= converged(ik)) then
230 conv = conv + st%group%psib(ib, ik)%nst
231 convb = convb + 1
232 else
233 exit
234 end if
235 end do
236 end if
237
239 end function get_first_unconverged_batch
240end module eigen_evolution_oct_m
241
242!! Local Variables:
243!! mode: f90
244!! coding: utf-8
245!! End:
double log(double __x) __attribute__((__nothrow__
double sqrt(double __x) __attribute__((__nothrow__
subroutine, public eigensolver_evolution(namespace, mesh, st, hm, space, ext_partners, te, iter, niter, converged, tau0, tau, time, propagator, variable_timestep, vks_old, normalization_energies, normalization_energies_prev, residual)
integer, parameter, public it_expmid
integer function get_first_unconverged_batch(st, hm, ik, converged)
subroutine exponential_imaginary_time(namespace, mesh, st, hm, te, converged, tau, normalization_energies)
real(real64), parameter, public m_two
Definition: global.F90:190
real(real64), parameter, public m_zero
Definition: global.F90:188
This module defines classes and functions for interaction partners.
A module to handle KS potential, without the external potential.
integer, parameter, public independent_particles
This module defines functions over batches of mesh functions.
Definition: mesh_batch.F90:116
subroutine, public zmesh_batch_dotp_vector(mesh, aa, bb, dot, reduce, cproduct)
calculate the vector of dot-products of mesh functions between two batches
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:414
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:616
integer pure function, public states_elec_block_max(st, ib)
return index of last state in block ib
integer pure function, public states_elec_block_min(st, ib)
return index of first state in block ib
type(type_t), public type_float
Definition: types.F90:133
type(type_t), public type_cmplx
Definition: types.F90:134
Describes mesh distribution to nodes.
Definition: mesh.F90:186
The states_elec_t class contains all electronic wave functions.
batches of electronic states
Definition: wfs_elec.F90:139
int true(void)