Octopus
exponential.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch
2!! Copyright (C) 2019 M. Oliveira
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 accel_oct_m
24 use batch_oct_m
26 use blas_oct_m
28 use debug_oct_m
29 use global_oct_m
30 use grid_oct_m
34 use, intrinsic :: iso_fortran_env
38 use math_oct_m
39 use mesh_oct_m
44 use parser_oct_m
48 use types_oct_m
50 use xc_oct_m
51
52 implicit none
53
54 private
55 public :: &
61 operator_t, &
63
64 integer, public, parameter :: &
65 EXP_LANCZOS = 2, &
66 exp_taylor = 3, &
68
69 type exponential_t
70 private
71 integer, public :: exp_method
72 real(real64) :: lanczos_tol
73 real(real64) :: chebyshev_tol
74 integer, public :: exp_order
75 integer :: arnoldi_gs
76 logical, public :: full_batch = .false.
77 contains
78 procedure :: apply_batch => exponential_apply_batch
79 procedure :: apply_single => exponential_apply_single
80 procedure :: apply_phi_batch => exponential_apply_phi_batch
81 end type exponential_t
82
83
84 type, abstract :: operator_t
85 type(namespace_t), pointer :: namespace
86 class(mesh_t), pointer :: mesh
87 contains
88 procedure(operator_apply), deferred :: apply
89 end type operator_t
90
91 abstract interface
92 subroutine operator_apply(this, psib, hpsib)
93 import operator_t
94 import batch_t
95 class(operator_t), intent(in) :: this
96 class(batch_t), intent(inout) :: psib
97 class(batch_t), intent(inout) :: hpsib
98 end subroutine operator_apply
99 end interface
100
101
102 type, extends(operator_t) :: hamiltonian_operator_t
103 class(hamiltonian_abst_t), pointer :: hm
104 contains
105 procedure :: apply => hamiltonian_operator_apply
106 procedure :: init => hamiltonian_operator_init
108
109 interface hamiltonian_operator_t
110 procedure hamiltonian_operator_constructor
111 end interface hamiltonian_operator_t
112
113contains
114
115 ! ---------------------------------------------------------
116 subroutine exponential_init(te, namespace, full_batch)
117 type(exponential_t), intent(out) :: te
118 type(namespace_t), intent(in) :: namespace
119 logical, optional, intent(in) :: full_batch
120
121 push_sub(exponential_init)
122
123 !%Variable TDExponentialMethod
124 !%Type integer
125 !%Default taylor
126 !%Section Time-Dependent::Propagation
127 !%Description
128 !% Method used to numerically calculate the exponential of the Hamiltonian,
129 !% a core part of the full algorithm used to approximate the evolution
130 !% operator, specified through the variable <tt>TDPropagator</tt>.
131 !% In the case of using the Magnus method, described below, the action of the exponential
132 !% of the Magnus operator is also calculated through the algorithm specified
133 !% by this variable.
134 !%Option lanczos 2
135 !% Allows for larger time-steps.
136 !% However, the larger the time-step, the longer the computational time per time-step.
137 !% In certain cases, if the time-step is too large, the code will emit a warning
138 !% whenever it considers that the evolution may not be properly proceeding --
139 !% the Lanczos process did not converge. The method consists in a Krylov
140 !% subspace approximation of the action of the exponential
141 !% (see M. Hochbruck and C. Lubich, <i>SIAM J. Numer. Anal.</i> <b>34</b>, 1911 (1997) for details).
142 !% The performance of the method is controlled by the tolerance (controlled by <tt>TDLanczosTol</tt>).
143 !% The smaller the tolerance, the more precisely the exponential
144 !% is calculated, but also the larger the dimension of the Arnoldi
145 !% subspace. If the maximum dimension (currently 200) is not enough to meet the criterion,
146 !% the above-mentioned warning is emitted.
147 !% Be aware that the larger the required dimension of the Krylov subspace, the larger
148 !% the memory required for this method. So if you run out of memory, try to reduce
149 !% the time step.
150 !%Option taylor 3
151 !% This method amounts to a straightforward application of the definition of
152 !% the exponential of an operator, in terms of its Taylor expansion.
153 !%
154 !% <math>\exp_{\rm STD} (-i\delta t H) = \sum_{i=0}^{k} {(-i\delta t)^i\over{i!}} H^i.</math>
155 !%
156 !% The order <i>k</i> is determined by variable <tt>TDExpOrder</tt>.
157 !% Some numerical considerations from <a href=http://www.phys.washington.edu/~bertsch/num3.ps>
158 !% Jeff Giansiracusa and George F. Bertsch</a>
159 !% suggest the 4th order as especially suitable and stable.
160 !%Option chebyshev 4
161 !% In principle, the Chebyshev expansion
162 !% of the exponential represents it more accurately than the canonical or standard expansion.
163 !% <tt>TDChebyshevTol</tt> determines the tolerance to which the expansion is computed.
164 !%
165 !% There exists a closed analytic form for the coefficients of the exponential in terms
166 !% of Chebyshev polynomials:
167 !%
168 !% <math>\exp_{\rm CHEB} \left( -i\delta t H \right) = \sum_{k=0}^{\infty} (2-\delta_{k0})(-i)^{k}J_k(\delta t) T_k(H),</math>
169 !%
170 !% where <math>J_k</math> are the Bessel functions of the first kind, and H has to be previously
171 !% scaled to <math>[-1,1]</math>.
172 !% See H. Tal-Ezer and R. Kosloff, <i>J. Chem. Phys.</i> <b>81</b>,
173 !% 3967 (1984); R. Kosloff, <i>Annu. Rev. Phys. Chem.</i> <b>45</b>, 145 (1994);
174 !% C. W. Clenshaw, <i>MTAC</i> <b>9</b>, 118 (1955).
175 !%End
176 call parse_variable(namespace, 'TDExponentialMethod', exp_taylor, te%exp_method)
177
178 select case (te%exp_method)
181 !%Variable TDChebyshevTol
182 !%Type float
183 !%Default 1e-10
184 !%Section Time-Dependent::Propagation
185 !%Description
186 !% An internal tolerance variable for the Chebyshev method. The smaller, the more
187 !% precisely the exponential is calculated and the more iterations are needed, i.e.,
188 !% it becomes more expensive. The expansion is terminated once the error estimate
189 !% is below this tolerance.
190 !%End
191 call parse_variable(namespace, 'TDChebyshevTol', 1e-10_real64, te%chebyshev_tol)
192 if (te%chebyshev_tol <= m_zero) call messages_input_error(namespace, 'TDChebyshevTol')
193 case (exp_lanczos)
194 !%Variable TDLanczosTol
195 !%Type float
196 !%Default 1e-6
197 !%Section Time-Dependent::Propagation
198 !%Description
199 !% An internal tolerance variable for the Lanczos method. The smaller, the more
200 !% precisely the exponential is calculated, and also the bigger the dimension
201 !% of the Krylov subspace needed to perform the algorithm. One should carefully
202 !% make sure that this value is not too big, or else the evolution will be
203 !% wrong.
204 !%End
205 call parse_variable(namespace, 'TDLanczosTol', 1e-6_real64, te%lanczos_tol)
206 if (te%lanczos_tol <= m_zero) call messages_input_error(namespace, 'TDLanczosTol')
207
208 case default
209 call messages_input_error(namespace, 'TDExponentialMethod')
210 end select
211 call messages_print_var_option('TDExponentialMethod', te%exp_method, namespace=namespace)
212
213 if (te%exp_method == exp_taylor) then
214 !%Variable TDExpOrder
215 !%Type integer
216 !%Default 4
217 !%Section Time-Dependent::Propagation
218 !%Description
219 !% For <tt>TDExponentialMethod</tt> = <tt>taylor</tt>,
220 !% the order to which the exponential is expanded.
221 !%End
222 call parse_variable(namespace, 'TDExpOrder', default__tdexporder, te%exp_order)
223 if (te%exp_order < 2) call messages_input_error(namespace, 'TDExpOrder')
224 else
225 if (parse_is_defined(namespace, 'TDExpOrder')) then
226 message(1) = "TDExpOrder is only relevant for TDExponentialMethod = taylor"
227 call messages_fatal(1, namespace=namespace)
228 end if
229 end if
230
231 te%arnoldi_gs = option__arnoldiorthogonalization__cgs
232 if (te%exp_method == exp_lanczos) then
233 !%Variable ArnoldiOrthogonalization
234 !%Type integer
235 !%Section Time-Dependent::Propagation
236 !%Description
237 !% The orthogonalization method used for the Arnoldi procedure.
238 !% Only for TDExponentialMethod = lanczos.
239 !%Option cgs 3
240 !% Classical Gram-Schmidt (CGS) orthogonalization.
241 !% The algorithm is defined in Giraud et al., Computers and Mathematics with Applications 50, 1069 (2005).
242 !%Option drcgs 5
243 !% Classical Gram-Schmidt orthogonalization with double-step reorthogonalization.
244 !% The algorithm is taken from Giraud et al., Computers and Mathematics with Applications 50, 1069 (2005).
245 !% According to this reference, this is much more precise than CGS or MGS algorithms.
246 !%End
247 call parse_variable(namespace, 'ArnoldiOrthogonalization', option__arnoldiorthogonalization__cgs, &
248 te%arnoldi_gs)
249 end if
250
251 ! do lanczos expansion for full batch?
252 te%full_batch = optional_default(full_batch, te%full_batch)
253
254 pop_sub(exponential_init)
255 end subroutine exponential_init
256
257 ! ---------------------------------------------------------
258 subroutine exponential_copy(teo, tei)
259 type(exponential_t), intent(inout) :: teo
260 type(exponential_t), intent(in) :: tei
261
262 push_sub(exponential_copy)
263
264 teo%exp_method = tei%exp_method
265 teo%lanczos_tol = tei%lanczos_tol
266 teo%exp_order = tei%exp_order
267 teo%arnoldi_gs = tei%arnoldi_gs
268
269 pop_sub(exponential_copy)
270 end subroutine exponential_copy
271
272 ! ---------------------------------------------------------
274 subroutine exponential_apply_single(te, namespace, mesh, hm, zpsi, ist, ik, deltat, imag_time)
275 class(exponential_t), intent(inout) :: te
276 type(namespace_t), intent(in) :: namespace
277 class(mesh_t), intent(in) :: mesh
278 type(hamiltonian_elec_t), intent(inout) :: hm
279 integer, intent(in) :: ist
280 integer, intent(in) :: ik
281 complex(real64), contiguous, intent(inout) :: zpsi(:, :)
282 real(real64), intent(in) :: deltat
283 logical, optional, intent(in) :: imag_time
284
285 type(wfs_elec_t) :: psib, inh_psib
286 complex(real64), allocatable :: zpsi_inh(:, :)
287
289
290 !We apply the phase only to np points, and the phase for the np+1 to np_part points
291 !will be treated as a phase correction in the Hamiltonian
292 if (hm%phase%is_allocated()) then
293 call hm%phase%apply_to_single(zpsi, mesh%np, hm%d%dim, ik, .false.)
294 end if
295
296 call wfs_elec_init(psib, hm%d%dim, ist, ist, zpsi, ik)
297
298 if (hamiltonian_elec_inh_term(hm)) then
299 safe_allocate(zpsi_inh(1:mesh%np_part, 1:hm%d%dim))
300 call states_elec_get_state(hm%inh_st, mesh, ist, ik, zpsi_inh(:, :))
301 call wfs_elec_init(inh_psib, hm%d%dim, ist, ist, zpsi_inh, ik)
302 call te%apply_batch(namespace, mesh, hm, psib, deltat, &
303 imag_time=imag_time, inh_psib=inh_psib)
304 call inh_psib%end()
305 safe_deallocate_a(zpsi_inh)
306 else
307 call te%apply_batch(namespace, mesh, hm, psib, deltat, &
308 imag_time=imag_time)
309 end if
310
311 call psib%end()
312
313 if (hm%phase%is_allocated()) then
314 call hm%phase%apply_to_single(zpsi, mesh%np, hm%d%dim, ik, .true.)
315 end if
316
318 end subroutine exponential_apply_single
319
320 ! ---------------------------------------------------------
337 ! ---------------------------------------------------------
338 subroutine exponential_apply_batch(te, namespace, mesh, hm, psib, deltat, psib2, deltat2, imag_time, inh_psib, op)
339 class(exponential_t), intent(inout) :: te
340 type(namespace_t), intent(in) :: namespace
341 class(mesh_t), intent(in) :: mesh
342 class(hamiltonian_abst_t), intent(inout) :: hm
343 class(batch_t), intent(inout) :: psib
344 real(real64), intent(in) :: deltat
345 class(batch_t), optional, intent(inout) :: psib2
346 real(real64), optional, intent(in) :: deltat2
347 logical, optional, intent(in) :: imag_time
348 class(batch_t), optional, intent(inout) :: inh_psib
349 class(operator_t), target, optional, intent(in) :: op
350
351 complex(real64) :: deltat_, deltat2_
352 class(chebyshev_function_t), pointer :: chebyshev_function, chebyshev_function_dt2
353 logical :: imag_time_
354 class(operator_t), pointer :: op_
355
357 call profiling_in("EXPONENTIAL_BATCH")
358
359 assert(psib%type() == type_cmplx)
360
361 assert(present(psib2) .eqv. present(deltat2))
362 if (present(inh_psib)) then
363 assert(inh_psib%nst == psib%nst)
364 end if
365
366 if (present(op)) then
367 op_ => op
368 else
369 op_ => hamiltonian_operator_t(namespace, mesh, hm)
370 end if
371
372 deltat2_ = cmplx(optional_default(deltat2, m_zero), m_zero, real64)
373
374 imag_time_ = optional_default(imag_time, .false.)
375 if (imag_time_) then
376 deltat_ = -m_zi*deltat
377 if (present(deltat2)) deltat2_ = m_zi*deltat2
378 else
379 deltat_ = cmplx(deltat, m_zero, real64)
380 if (present(deltat2)) deltat2_ = cmplx(deltat2, m_zero, real64)
381 end if
382
383 if (.not. hm%is_hermitian() .and. te%exp_method == exp_chebyshev) then
384 write(message(1), '(a)') 'The Chebyshev expansion cannot be used for non-Hermitian operators.'
385 write(message(2), '(a)') 'Please use the Lanczos exponentiation scheme ("TDExponentialMethod = lanczos")'
386 write(message(3), '(a)') 'or the Taylor expansion ("TDExponentialMethod = taylor") method.'
387 call messages_fatal(3, namespace=namespace)
388 end if
389
390 select case (te%exp_method)
391 case (exp_taylor)
392 ! Note that delttat2_ is only initialized if deltat2 is present.
393 if (present(deltat2)) then
394 call exponential_taylor_series_batch(te, namespace, mesh, hm, psib, deltat_, op_, &
395 psib2, deltat2_)
396 else
397 call exponential_taylor_series_batch(te, namespace, mesh, hm, psib, deltat_, op_)
398 end if
399 if (present(inh_psib)) then
400 if (present(deltat2)) then
401 call exponential_taylor_series_batch(te, namespace, mesh, hm, psib, deltat_, op_, &
402 psib2, deltat2_, inh_psib)
403 else
404 call exponential_taylor_series_batch(te, namespace, mesh, hm, psib, deltat_, op_, &
405 inh_psib=inh_psib)
406 end if
407 end if
408
409 case (exp_lanczos)
410 if (present(psib2)) call psib%copy_data_to(mesh%np, psib2)
411 call exponential_lanczos_batch(te, namespace, mesh, hm, psib, deltat_, op_)
412 if (present(inh_psib)) then
413 call exponential_lanczos_batch(te, namespace, mesh, hm, psib, deltat_, op_, inh_psib)
414 end if
415 if (present(psib2)) then
416 call exponential_lanczos_batch(te, namespace, mesh, hm, psib2, deltat2_, op_)
417 if (present(inh_psib)) then
418 call exponential_lanczos_batch(te, namespace, mesh, hm, psib2, deltat2_, op_, inh_psib)
419 end if
420 end if
421
422 case (exp_chebyshev)
423 if (present(inh_psib)) then
424 write(message(1), '(a)') 'Chebyshev exponential ("TDExponentialMethod = chebyshev")'
425 write(message(2), '(a)') 'with inhomogeneous term is not implemented'
426 call messages_fatal(2, namespace=namespace)
427 end if
428 ! initialize classes for computing coefficients
429 if (imag_time_) then
430 chebyshev_function => chebyshev_exp_imagtime_t(hm%spectral_half_span, hm%spectral_middle_point, deltat)
431 if (present(psib2)) then
432 chebyshev_function_dt2 => chebyshev_exp_imagtime_t(hm%spectral_half_span, hm%spectral_middle_point, deltat2)
433 end if
434 else
435 chebyshev_function => chebyshev_exp_t(hm%spectral_half_span, hm%spectral_middle_point, deltat)
436 if (present(psib2)) then
437 chebyshev_function_dt2 => chebyshev_exp_t(hm%spectral_half_span, hm%spectral_middle_point, deltat2)
438 end if
439 end if
440 if (present(psib2)) call psib%copy_data_to(mesh%np, psib2)
441 call exponential_cheby_batch(te, namespace, mesh, hm, psib, chebyshev_function, op_)
442 deallocate(chebyshev_function)
443 if (present(psib2)) then
444 call exponential_cheby_batch(te, namespace, mesh, hm, psib2, chebyshev_function_dt2, op_)
445 deallocate(chebyshev_function_dt2)
446 end if
447
448 end select
449
450 if (.not.present(op)) then
451 safe_deallocate_p(op_)
452 end if
453
454 call profiling_out("EXPONENTIAL_BATCH")
456 end subroutine exponential_apply_batch
457
458 ! ---------------------------------------------------------
459 subroutine exponential_taylor_series_batch(te, namespace, mesh, hm, psib, deltat, op, psib2, deltat2, inh_psib, phik_shift)
460 type(exponential_t), intent(inout) :: te
461 type(namespace_t), intent(in) :: namespace
462 class(mesh_t), intent(in) :: mesh
463 class(hamiltonian_abst_t), intent(inout) :: hm
464 class(batch_t), intent(inout) :: psib
465 complex(real64), intent(in) :: deltat
466 class(operator_t), intent(in) :: op
467 class(batch_t), optional, intent(inout) :: psib2
468 complex(real64), optional, intent(in) :: deltat2
469 class(batch_t), optional, intent(inout) :: inh_psib
470 integer, optional, intent(in) :: phik_shift
471
472 complex(real64) :: zfact, zfact2
473 integer :: iter, denom, phik_shift_
474 logical :: zfact_is_real
475 class(batch_t), allocatable :: psi1b, hpsi1b
476
478 call profiling_in("EXP_TAYLOR_BATCH")
479
480 call psib%clone_to(psi1b)
481 call psib%clone_to(hpsi1b)
482
483 zfact = m_z1
484 zfact2 = m_z1
485 zfact_is_real = abs(deltat-real(deltat, real64)) < m_epsilon
486
487 if (present(psib2)) call psib%copy_data_to(mesh%np, psib2)
488
489 if (present(inh_psib)) then
490 zfact = zfact*deltat
491 call batch_axpy(mesh%np, real(zfact, real64), inh_psib, psib)
492
493 if (present(psib2)) then
494 zfact2 = zfact2*deltat2
495 call batch_axpy(mesh%np, real(zfact2, real64), inh_psib, psib2)
496 end if
497 end if
498
499 ! shift the denominator by this shift for the phi_k functions
500 phik_shift_ = optional_default(phik_shift, 0)
501
502 do iter = 1, te%exp_order
503 denom = iter+phik_shift_
504 if (present(inh_psib)) denom = denom + 1
505 zfact = zfact*(-m_zi*deltat)/denom
506 if (present(deltat2)) zfact2 = zfact2*(-m_zi*deltat2)/denom
507 zfact_is_real = .not. zfact_is_real
508 ! FIXME: need a test here for runaway exponential, e.g. for too large dt.
509 ! in runaway case the problem is really hard to trace back: the positions
510 ! go haywire on the first step of dynamics (often NaN) and with debugging options
511 ! the code stops in ZAXPY below without saying why.
512
513 if (iter /= 1) then
514 call op%apply(psi1b, hpsi1b)
515 else
516 if (present(inh_psib)) then
517 call op%apply(inh_psib, hpsi1b)
518 else
519 call op%apply(psib, hpsi1b)
520 end if
521 end if
522
523 if (zfact_is_real) then
524 call batch_axpy(mesh%np, real(zfact, real64), hpsi1b, psib)
525 if (present(psib2)) call batch_axpy(mesh%np, real(zfact2, real64), hpsi1b, psib2)
526 else
527 call batch_axpy(mesh%np, zfact, hpsi1b, psib)
528 if (present(psib2)) call batch_axpy(mesh%np, zfact2, hpsi1b, psib2)
529 end if
530
531 if (iter /= te%exp_order) call hpsi1b%copy_data_to(mesh%np, psi1b)
532
533 end do
534
535 call psi1b%end()
536 call hpsi1b%end()
537 safe_deallocate_a(psi1b)
538 safe_deallocate_a(hpsi1b)
539
540 call profiling_out("EXP_TAYLOR_BATCH")
543
544 subroutine exponential_lanczos_batch(te, namespace, mesh, hm, psib, deltat, op, inh_psib)
545 type(exponential_t), intent(inout) :: te
546 type(namespace_t), intent(in) :: namespace
547 class(mesh_t), intent(in) :: mesh
548 class(hamiltonian_abst_t), intent(inout) :: hm
549 class(batch_t), intent(inout) :: psib
550 complex(real64), intent(in) :: deltat
551 class(operator_t), intent(in) :: op
552 class(batch_t), optional, intent(in) :: inh_psib
553
554 class(batch_t), allocatable :: tmpb
555
557
558 if (present(inh_psib)) then
559 call inh_psib%clone_to(tmpb, copy_data=.true.)
560 ! psib = psib + deltat * phi1(-i*deltat*H) inh_psib
561 call exponential_lanczos_function_batch(te, namespace, mesh, hm, tmpb, deltat, phi1, op)
562 call batch_axpy(mesh%np, real(deltat, real64), tmpb, psib)
563 call tmpb%end()
564 safe_deallocate_a(tmpb)
565 else
566 call exponential_lanczos_function_batch(te, namespace, mesh, hm, psib, deltat, exponential, op)
567 end if
568
570 end subroutine exponential_lanczos_batch
571
572 ! ---------------------------------------------------------
584 subroutine exponential_lanczos_function_batch(te, namespace, mesh, hm, psib, deltat, fun, op)
585 type(exponential_t), intent(inout) :: te
586 type(namespace_t), intent(in) :: namespace
587 class(mesh_t), intent(in) :: mesh
588 class(hamiltonian_abst_t), intent(inout) :: hm
589 class(batch_t), intent(inout) :: psib
590 complex(real64), intent(in) :: deltat
591 interface
592 complex(real64) function fun(z)
593 import real64
594 complex(real64), intent(in) :: z
595 end
596 end interface
597 class(operator_t), intent(in) :: op
598
599 integer :: iter, l, ii, ist, max_initialized
600 complex(real64), allocatable :: hamilt(:,:,:), expo(:,:,:)
601 real(real64), allocatable :: beta(:), res(:), norm(:)
602 integer, parameter :: max_order = 200
603 type(batch_p_t), allocatable :: vb(:) ! Krylov subspace vectors
604
606 call profiling_in("EXP_LANCZOS_FUN_BATCH")
607
608 if (te%exp_method /= exp_lanczos) then
609 message(1) = "The exponential method needs to be set to Lanzcos (TDExponentialMethod=lanczos)."
610 call messages_fatal(1)
611 end if
612
613 safe_allocate(beta(1:psib%nst))
614 safe_allocate(res(1:psib%nst))
615 safe_allocate(norm(1:psib%nst))
616 safe_allocate(vb(1:max_order))
617 call psib%clone_to(vb(1)%p)
618 max_initialized = 1
619
620 call psib%copy_data_to(mesh%np, vb(1)%p, async=.true.)
621 call mesh_batch_nrm2(mesh, vb(1)%p, beta)
622
623 if (te%full_batch) beta = norm2(beta)
624
625 ! If we have a null vector, no need to compute the action of the exponential.
626 if (all(abs(beta) <= 1.0e-12_real64)) then
627 safe_deallocate_a(beta)
628 safe_deallocate_a(res)
629 safe_deallocate_a(norm)
630 call vb(1)%p%end()
631 safe_deallocate_a(vb)
632 call profiling_out("EXP_LANCZOS_FUN_BATCH")
634 return
635 end if
636
637 call batch_scal(mesh%np, m_one/beta, vb(1)%p, a_full = .false.)
638
639 safe_allocate(hamilt(1:max_order+1, 1:max_order+1, 1:psib%nst))
640 safe_allocate( expo(1:max_order+1, 1:max_order+1, 1:psib%nst))
641
642 ! This is the Lanczos loop...
643 do iter = 1, max_order-1
644 call psib%clone_to(vb(iter + 1)%p)
645 max_initialized = iter + 1
646
647 ! to apply the operator (default is Hamiltonian)
648 call op%apply(vb(iter)%p, vb(iter+1)%p)
649
650 ! We use either the Lanczos method (Hermitian case) or the Arnoldi method
651 if (hm%is_hermitian()) then
652 l = max(1, iter - 1)
653 hamilt(1:max(l-1, 1), iter, 1:psib%nst) = m_zero
654 else
655 l = 1
656 if (iter > 2) then
657 hamilt(iter, 1:iter-2, 1:psib%nst) = m_zero
658 end if
659 end if
660
661 ! Orthogonalize against previous vectors
662 call zmesh_batch_orthogonalization(mesh, iter - l + 1, vb(l:iter), vb(iter+1)%p, &
663 normalize = .false., overlap = hamilt(l:iter, iter, 1:psib%nst), norm = hamilt(iter + 1, iter, 1:psib%nst), &
664 gs_scheme = te%arnoldi_gs, full_batch = te%full_batch)
665
666 ! We now need to compute exp(Hm), where Hm is the projection of the linear transformation
667 ! of the Hamiltonian onto the Krylov subspace Km
668 ! See Eq. 4
669 !
670 ! Note that in the Hermitian case, we use the Lanczos algorithm that requires
671 ! only a tridiagonal matrix. Else we have an upper Hessenberg matrix.
672 do ii = 1, psib%nst
673 call zlalg_matrix_function(iter, -m_zi*deltat, hamilt(:,:,ii), expo(:,:,ii), fun, &
674 hm%is_hermitian(), tridiagonal=hm%is_hermitian())
675 res(ii) = abs(hamilt(iter + 1, iter, ii) * abs(expo(iter, 1, ii)))
676 end do !ii
677
678 ! We now estimate the error we made. This is given by the formula denoted Er2 in Sec. 5.2
679 if (all(abs(hamilt(iter + 1, iter, :)) < 1.0e4_real64*m_epsilon)) exit ! "Happy breakdown"
680 ! We normalize only if the norm is non-zero
681 ! see http://www.netlib.org/utk/people/JackDongarra/etemplates/node216.html#alg:arn0
682 norm = m_one
683 do ist = 1, psib%nst
684 if (abs(hamilt(iter + 1, iter, ist)) >= 1.0e4_real64 * m_epsilon) then
685 norm(ist) = m_one / abs(hamilt(iter + 1, iter, ist))
686 end if
687 end do
688
689 call batch_scal(mesh%np, norm, vb(iter+1)%p, a_full = .false.)
690
691 if (iter > 3 .and. all(res < te%lanczos_tol)) exit
692
693 end do !iter
694
695 if (iter == max_order) then ! Here one should consider the possibility of the happy breakdown.
696 write(message(1),'(a,i5,a,es9.2)') 'Lanczos exponential expansion did not converge after ', iter, &
697 ' iterations. Residual: ', maxval(res)
698 call messages_warning(1, namespace=namespace)
699 else
700 write(message(1),'(a,i5)') 'Debug: Lanczos exponential iterations: ', iter
701 call messages_info(1, namespace=namespace, debug_only=.true.)
702 end if
703
704 ! See Eq. 4 for the expression here
705 ! zpsi = nrm * V * expo(1:iter, 1) = nrm * V * expo * V^(T) * zpsi
706 call batch_scal(mesh%np, expo(1,1,1:psib%nst), psib, a_full = .false.)
707 ! TODO: We should have a routine batch_gemv for improved performance (see #1070 on gitlab)
708 do ii = 2, iter
709 call batch_axpy(mesh%np, beta(1:psib%nst)*expo(ii,1,1:psib%nst), vb(ii)%p, psib, a_full = .false.)
710 ! In order to apply the two exponentials, we must store the eigenvalues and eigenvectors given by zlalg_exp
711 ! And to recontruct here the exp(i*dt*H) for deltat2
712 end do
713
714 do ii = 1, max_initialized
715 call vb(ii)%p%end()
716 end do
717
718 safe_deallocate_a(vb)
719 safe_deallocate_a(hamilt)
720 safe_deallocate_a(expo)
721 safe_deallocate_a(beta)
722 safe_deallocate_a(res)
723 safe_deallocate_a(norm)
724
725 call accel_finish()
726
727 call profiling_out("EXP_LANCZOS_FUN_BATCH")
728
731
732
733 ! ---------------------------------------------------------
745 subroutine exponential_cheby_batch(te, namespace, mesh, hm, psib, chebyshev_function, op)
746 type(exponential_t), intent(inout) :: te
747 type(namespace_t), intent(in) :: namespace
748 class(mesh_t), intent(in) :: mesh
749 class(hamiltonian_abst_t), intent(inout) :: hm
750 class(batch_t), intent(inout) :: psib
751 class(chebyshev_function_t), intent(in) :: chebyshev_function
752 class(operator_t), intent(in) :: op
753
754 integer :: j, order_needed
755 complex(real64) :: coefficient
756 complex(real64), allocatable :: coefficients(:)
757 real(real64) :: error
758 class(batch_t), allocatable, target :: psi0, psi1, psi2
759 class(batch_t), pointer :: psi_n, psi_n1, psi_n2
760 integer, parameter :: max_order = 200
761
763 call profiling_in("EXP_CHEBY_BATCH")
764
765 call psib%clone_to(psi0)
766 call psib%clone_to(psi1)
767 call psib%clone_to(psi2)
768 call psib%copy_data_to(mesh%np, psi0)
769
770 order_needed = max_order
771 do j = 1, max_order
772 error = chebyshev_function%get_error(j)
773 if (error > m_zero .and. error < te%chebyshev_tol) then
774 order_needed = j
775 exit
776 end if
777 end do
778
779 call chebyshev_function%get_coefficients(j, coefficients)
780
781 ! zero-order term
782 call batch_scal(mesh%np, coefficients(0), psib)
783 ! first-order term
784 ! shifted Hamiltonian
785 call op%apply(psi0, psi1)
786 call batch_axpy(mesh%np, -hm%spectral_middle_point, psi0, psi1)
787 call batch_scal(mesh%np, m_one/hm%spectral_half_span, psi1)
788 ! accumulate result
789 call batch_axpy(mesh%np, coefficients(1), psi1, psib)
790
791 ! use pointers to avoid copies
792 psi_n => psi2
793 psi_n1 => psi1
794 psi_n2 => psi0
795
796 do j = 2, order_needed
797 ! compute shifted Hamiltonian and Chebyshev recurrence formula
798 call op%apply(psi_n1, psi_n)
799 call batch_axpy(mesh%np, -hm%spectral_middle_point, psi_n1, psi_n)
800 call batch_xpay(mesh%np, psi_n2, -m_two/hm%spectral_half_span, psi_n)
801 call batch_scal(mesh%np, -m_one, psi_n)
802
803 ! accumulate result
804 call batch_axpy(mesh%np, coefficients(j), psi_n, psib)
805
806 ! shift pointers for the three-term recurrence, this avoids copies
807 if (mod(j, 3) == 2) then
808 psi_n => psi0
809 psi_n1 => psi2
810 psi_n2 => psi1
811 else if (mod(j, 3) == 1) then
812 psi_n => psi2
813 psi_n1 => psi1
814 psi_n2 => psi0
815 else
816 psi_n => psi1
817 psi_n1 => psi0
818 psi_n2 => psi2
819 end if
820 end do
821
822 if (order_needed == max_order) then
823 write(message(1),'(a,i5,a,es9.2)') 'Chebyshev exponential expansion did not converge after ', j, &
824 ' iterations. Coefficient: ', coefficient
825 call messages_warning(1, namespace=namespace)
826 else
827 write(message(1),'(a,i5)') 'Debug: Chebyshev exponential iterations: ', j
828 call messages_info(1, namespace=namespace, debug_only=.true.)
829 end if
830
831 call psi0%end()
832 call psi1%end()
833 call psi2%end()
834 safe_deallocate_a(psi0)
835 safe_deallocate_a(psi1)
836 safe_deallocate_a(psi2)
837
838 safe_deallocate_a(coefficients)
839
840 call profiling_out("EXP_CHEBY_BATCH")
841
843 end subroutine exponential_cheby_batch
844
845 ! ---------------------------------------------------------
849 subroutine exponential_apply_all(te, namespace, gr, hm, st, deltat, order)
850 type(exponential_t), intent(inout) :: te
851 type(namespace_t), intent(in) :: namespace
852 type(grid_t), intent(inout) :: gr
853 type(hamiltonian_elec_t), intent(inout) :: hm
854 type(states_elec_t), intent(inout) :: st
855 real(real64), intent(in) :: deltat
856 integer, optional, intent(inout) :: order
857
858 integer :: ik, ib, i
859 real(real64) :: zfact
860
861 type(states_elec_t) :: st1, hst1
862
863 push_sub(exponential_apply_all)
864
865 assert(te%exp_method == exp_taylor)
866
867 call states_elec_copy(st1, st)
868 call states_elec_copy(hst1, st)
869
870 zfact = m_one
871 do i = 1, te%exp_order
872 zfact = zfact * deltat / i
873
874 if (i == 1) then
875 call zhamiltonian_elec_apply_all(hm, namespace, gr, st, hst1)
876 else
877 call zhamiltonian_elec_apply_all(hm, namespace, gr, st1, hst1)
878 end if
879
880 do ik = st%d%kpt%start, st%d%kpt%end
881 do ib = st%group%block_start, st%group%block_end
882 call batch_scal2v(gr%np, -m_zi, hst1%group%psib(ib, ik), st1%group%psib(ib, ik), conjugate_xx = .false.)
883 call batch_axpy(gr%np, zfact, st1%group%psib(ib, ik), st%group%psib(ib, ik))
884 end do
885 end do
886
887 end do
888 ! End of Taylor expansion loop.
889
890 call states_elec_end(st1)
891 call states_elec_end(hst1)
892
893 ! We now add the inhomogeneous part, if present.
894 if (hamiltonian_elec_inh_term(hm)) then
895 !write(*, *) 'Now we apply the inhomogeneous term...'
896
897 call states_elec_copy(st1, hm%inh_st)
898 call states_elec_copy(hst1, hm%inh_st)
899
900
901 do ik = st%d%kpt%start, st%d%kpt%end
902 do ib = st%group%block_start, st%group%block_end
903 call batch_axpy(gr%np, deltat, st1%group%psib(ib, ik), st%group%psib(ib, ik))
904 end do
905 end do
906
907 zfact = m_one
908 do i = 1, te%exp_order
909 zfact = zfact * deltat / (i+1)
910
911 if (i == 1) then
912 call zhamiltonian_elec_apply_all(hm, namespace, gr, hm%inh_st, hst1)
913 else
914 call zhamiltonian_elec_apply_all(hm, namespace, gr, st1, hst1)
915 end if
916
917 do ik = st%d%kpt%start, st%d%kpt%end
918 do ib = st%group%block_start, st%group%block_end
919 call batch_scal2v(gr%np, -m_zi, hst1%group%psib(ib, ik), st1%group%psib(ib, ik), conjugate_xx = .false.)
920 call batch_axpy(gr%np, deltat * zfact, st1%group%psib(ib, ik), st%group%psib(ib, ik))
921 end do
922 end do
923
924 end do
925
926 call states_elec_end(st1)
927 call states_elec_end(hst1)
928
929 end if
930
931 if (present(order)) order = te%exp_order*st%nik*st%nst ! This should be the correct number
932
933 pop_sub(exponential_apply_all)
934 end subroutine exponential_apply_all
935
936 subroutine exponential_apply_phi_batch(te, namespace, mesh, hm, psib, deltat, k, op)
937 class(exponential_t), intent(inout) :: te
938 type(namespace_t), intent(in) :: namespace
939 class(mesh_t), intent(in) :: mesh
940 class(hamiltonian_abst_t), intent(inout) :: hm
941 class(batch_t), intent(inout) :: psib
942 real(real64), intent(in) :: deltat
943 integer, intent(in) :: k
944 class(operator_t), target,optional, intent(in) :: op
945
946 class(chebyshev_function_t), pointer :: chebyshev_function
947 complex(real64) :: deltat_
948 class(operator_t), pointer :: op_
949
950 push_sub_with_profile(exponential_apply_phi_batch)
951
952 assert(psib%type() == type_cmplx)
953
954 if (.not. hm%is_hermitian() .and. te%exp_method == exp_chebyshev) then
955 write(message(1), '(a)') 'The Chebyshev expansion for the exponential will only converge if the imaginary'
956 write(message(2), '(a)') 'eigenvalues are small enough compared to the span of the real eigenvalues,'
957 write(message(3), '(a)') 'i.e., for ratios smaller than about 1e-3.'
958 write(message(4), '(a)') 'The Lanczos method ("TDExponentialMethod = lanczos") is guaranteed to'
959 write(message(5), '(a)') 'always converge in this case.'
960 call messages_warning(5, namespace=namespace)
961 end if
962
963 if (present(op)) then
964 op_ => op
965 else
966 op_ => hamiltonian_operator_t(namespace, mesh, hm)
967 end if
968
969 deltat_ = cmplx(deltat, m_zero, real64)
970
971 select case (te%exp_method)
972 case (exp_taylor)
973 call exponential_taylor_series_batch(te, namespace, mesh, hm, psib, deltat_, op_, phik_shift=k)
974
975 case (exp_lanczos)
976 if (k == 1) then
977 call exponential_lanczos_function_batch(te, namespace, mesh, hm, psib, deltat_, phi1, op_)
978 else if (k == 2) then
979 call exponential_lanczos_function_batch(te, namespace, mesh, hm, psib, deltat_, phi2, op_)
980 else
981 write(message(1), '(a)') 'Lanczos expansion not implemented for phi_k, k > 2'
982 call messages_fatal(1, namespace=namespace)
983 end if
984
985 case (exp_chebyshev)
986 if (k == 1) then
987 chebyshev_function => chebyshev_numerical_t(hm%spectral_half_span, hm%spectral_middle_point, deltat, phi1)
988 else if (k == 2) then
989 chebyshev_function => chebyshev_numerical_t(hm%spectral_half_span, hm%spectral_middle_point, deltat, phi2)
990 else
991 write(message(1), '(a)') 'Chebyshev expansion not implemented for phi_k, k > 2'
992 call messages_fatal(1, namespace=namespace)
993 end if
994 call exponential_cheby_batch(te, namespace, mesh, hm, psib, chebyshev_function, op_)
995 deallocate(chebyshev_function)
996 end select
997
998 if (.not.present(op)) then
999 safe_deallocate_p(op_)
1000 end if
1001
1002 pop_sub_with_profile(exponential_apply_phi_batch)
1003 end subroutine exponential_apply_phi_batch
1004
1005 function hamiltonian_operator_constructor(namespace, mesh, hm) result(this)
1006 type(namespace_t), target, intent(in) :: namespace
1007 class(mesh_t), target, intent(in) :: mesh
1008 class(hamiltonian_abst_t), target, intent(in) :: hm
1009 type(hamiltonian_operator_t), pointer :: this
1010
1012
1013 allocate(this)
1014 call this%init(namespace, mesh, hm)
1015
1018
1019 subroutine hamiltonian_operator_init(this, namespace, mesh, hm)
1020 class(hamiltonian_operator_t), intent(inout) :: this
1021 type(namespace_t), target, intent(in) :: namespace
1022 class(mesh_t), target, intent(in) :: mesh
1023 class(hamiltonian_abst_t), target, intent(in) :: hm
1024
1026
1027 this%mesh => mesh
1028 this%namespace => namespace
1029 this%hm => hm
1030
1032 end subroutine hamiltonian_operator_init
1033
1034 subroutine hamiltonian_operator_apply(this, psib, hpsib)
1035 class(hamiltonian_operator_t), intent(in) :: this
1036 class(batch_t), intent(inout) :: psib
1037 class(batch_t), intent(inout) :: hpsib
1038
1040
1041 call this%hm%zapply(this%namespace, this%mesh, psib, hpsib)
1042
1044 end subroutine hamiltonian_operator_apply
1045
1046end module exponential_oct_m
1047
1048!! Local Variables:
1049!! mode: f90
1050!! coding: utf-8
1051!! End:
batchified version of the BLAS axpy routine:
Definition: batch_ops.F90:159
batchified scale with optional conjugation:
Definition: batch_ops.F90:181
scale a batch by a constant or vector
Definition: batch_ops.F90:167
batchified version of
Definition: batch_ops.F90:187
subroutine, public accel_finish()
Definition: accel.F90:1124
This module implements batches of mesh functions.
Definition: batch.F90:135
This module implements common operations on batches of mesh functions.
Definition: batch_ops.F90:118
This module contains interfaces for BLAS routines You should not use these routines directly....
Definition: blas.F90:120
subroutine, public exponential_copy(teo, tei)
subroutine, public exponential_apply_all(te, namespace, gr, hm, st, deltat, order)
Note that this routine not only computes the exponential, but also an extra term if there is a inhomo...
type(hamiltonian_operator_t) function, pointer hamiltonian_operator_constructor(namespace, mesh, hm)
subroutine exponential_cheby_batch(te, namespace, mesh, hm, psib, chebyshev_function, op)
Calculates the exponential of the Hamiltonian through an expansion in Chebyshev polynomials.
subroutine exponential_apply_phi_batch(te, namespace, mesh, hm, psib, deltat, k, op)
subroutine exponential_apply_batch(te, namespace, mesh, hm, psib, deltat, psib2, deltat2, imag_time, inh_psib, op)
This routine performs the operation:
subroutine hamiltonian_operator_init(this, namespace, mesh, hm)
subroutine, public exponential_lanczos_function_batch(te, namespace, mesh, hm, psib, deltat, fun, op)
Compute fun(H) psib, i.e. the application of a function of the Hamiltonian to a batch.
subroutine exponential_taylor_series_batch(te, namespace, mesh, hm, psib, deltat, op, psib2, deltat2, inh_psib, phik_shift)
subroutine, public exponential_init(te, namespace, full_batch)
integer, parameter, public exp_taylor
integer, parameter, public exp_chebyshev
subroutine exponential_apply_single(te, namespace, mesh, hm, zpsi, ist, ik, deltat, imag_time)
Wrapper to batchified routine for applying exponential to an array.
subroutine exponential_lanczos_batch(te, namespace, mesh, hm, psib, deltat, op, inh_psib)
subroutine hamiltonian_operator_apply(this, psib, hpsib)
real(real64), parameter, public m_two
Definition: global.F90:202
real(real64), parameter, public m_zero
Definition: global.F90:200
complex(real64), parameter, public m_zi
Definition: global.F90:214
real(real64), parameter, public m_epsilon
Definition: global.F90:216
complex(real64), parameter, public m_z1
Definition: global.F90:211
real(real64), parameter, public m_one
Definition: global.F90:201
This module implements the underlying real-space grid.
Definition: grid.F90:119
This module defines an abstract class for Hamiltonians.
subroutine, public zhamiltonian_elec_apply_all(hm, namespace, gr, st, hst)
pure logical function, public hamiltonian_elec_inh_term(hm)
subroutine, public zlalg_matrix_function(n, factor, a, fun_a, fun, hermitian, tridiagonal)
This routine calculates a function of a matrix by using an eigenvalue decomposition.
Definition: lalg_adv.F90:1953
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
complex(real64) pure function, public phi2(z)
Compute phi2(z) = (phi1(z)-1)/z = (exp(z) - z - 1)/z^2.
Definition: math.F90:930
complex(real64) pure function, public exponential(z)
Wrapper for exponential.
Definition: math.F90:899
complex(real64) pure function, public phi1(z)
Compute phi1(z) = (exp(z)-1)/z.
Definition: math.F90:911
This module defines functions over batches of mesh functions.
Definition: mesh_batch.F90:118
subroutine, public mesh_batch_nrm2(mesh, aa, nrm2, reduce)
Calculate the norms (norm2, not the square!) of a batch of mesh functions.
Definition: mesh_batch.F90:178
subroutine, public zmesh_batch_orthogonalization(mesh, nst, psib, phib, normalize, overlap, norm, gs_scheme, full_batch)
Orthonormalizes states of phib to the orbitals of nst batches of psi.
This module defines various routines, operating on mesh functions.
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
subroutine, public messages_warning(no_lines, all_nodes, namespace)
Definition: messages.F90:525
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:691
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
logical function, public parse_is_defined(namespace, name)
Definition: parser.F90:463
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:631
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:554
subroutine, public states_elec_end(st)
finalize the states_elec_t object
subroutine, public states_elec_copy(stout, stin, exclude_wfns, exclude_eigenval, special)
make a (selective) copy of a states_elec_t object
type(type_t), parameter, public type_cmplx
Definition: types.F90:136
Definition: xc.F90:120
Class defining batches of mesh functions.
Definition: batch.F90:161
Description of the grid, containing information on derivatives, stencil, and symmetries.
Definition: grid.F90:171
The abstract Hamiltonian class defines a skeleton for specific implementations.
Describes mesh distribution to nodes.
Definition: mesh.F90:187
The states_elec_t class contains all electronic wave functions.
batches of electronic states
Definition: wfs_elec.F90:141
int true(void)