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)) < 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), inh_psib, psib)
492
493 if (present(psib2)) then
494 zfact2 = zfact2*deltat2
495 call batch_axpy(mesh%np, real(zfact2), 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), hpsi1b, psib)
525 if (present(psib2)) call batch_axpy(mesh%np, real(zfact2), 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 else
565 call exponential_lanczos_function_batch(te, namespace, mesh, hm, psib, deltat, exponential, op)
566 end if
567
569 end subroutine exponential_lanczos_batch
570
571 ! ---------------------------------------------------------
583 subroutine exponential_lanczos_function_batch(te, namespace, mesh, hm, psib, deltat, fun, op)
584 type(exponential_t), intent(inout) :: te
585 type(namespace_t), intent(in) :: namespace
586 class(mesh_t), intent(in) :: mesh
587 class(hamiltonian_abst_t), intent(inout) :: hm
588 class(batch_t), intent(inout) :: psib
589 complex(real64), intent(in) :: deltat
590 interface
591 complex(real64) function fun(z)
592 import real64
593 complex(real64), intent(in) :: z
594 end
595 end interface
596 class(operator_t), intent(in) :: op
597
598 integer :: iter, l, ii, ist, max_initialized
599 complex(real64), allocatable :: hamilt(:,:,:), expo(:,:,:)
600 real(real64), allocatable :: beta(:), res(:), norm(:)
601 integer, parameter :: max_order = 200
602 type(batch_p_t), allocatable :: vb(:) ! Krylov subspace vectors
603
605 call profiling_in("EXP_LANCZOS_FUN_BATCH")
606
607 if (te%exp_method /= exp_lanczos) then
608 message(1) = "The exponential method needs to be set to Lanzcos (TDExponentialMethod=lanczos)."
609 call messages_fatal(1)
610 end if
611
612 safe_allocate(beta(1:psib%nst))
613 safe_allocate(res(1:psib%nst))
614 safe_allocate(norm(1:psib%nst))
615 safe_allocate(vb(1:max_order))
616 call psib%clone_to(vb(1)%p)
617 max_initialized = 1
618
619 call psib%copy_data_to(mesh%np, vb(1)%p, async=.true.)
620 call mesh_batch_nrm2(mesh, vb(1)%p, beta)
621
622 if (te%full_batch) beta = norm2(beta)
623
624 ! If we have a null vector, no need to compute the action of the exponential.
625 if (all(abs(beta) <= 1.0e-12_real64)) then
626 safe_deallocate_a(beta)
627 safe_deallocate_a(res)
628 safe_deallocate_a(norm)
629 call vb(1)%p%end()
630 safe_deallocate_a(vb)
631 call profiling_out("EXP_LANCZOS_FUN_BATCH")
633 return
634 end if
635
636 call batch_scal(mesh%np, m_one/beta, vb(1)%p, a_full = .false.)
637
638 safe_allocate(hamilt(1:max_order+1, 1:max_order+1, 1:psib%nst))
639 safe_allocate( expo(1:max_order+1, 1:max_order+1, 1:psib%nst))
640
641 ! This is the Lanczos loop...
642 do iter = 1, max_order-1
643 call psib%clone_to(vb(iter + 1)%p)
644 max_initialized = iter + 1
645
646 ! to apply the operator (default is Hamiltonian)
647 call op%apply(vb(iter)%p, vb(iter+1)%p)
648
649 ! We use either the Lanczos method (Hermitian case) or the Arnoldi method
650 if (hm%is_hermitian()) then
651 l = max(1, iter - 1)
652 hamilt(1:max(l-1, 1), iter, 1:psib%nst) = m_zero
653 else
654 l = 1
655 if (iter > 2) then
656 hamilt(iter, 1:iter-2, 1:psib%nst) = m_zero
657 end if
658 end if
659
660 ! Orthogonalize against previous vectors
661 call zmesh_batch_orthogonalization(mesh, iter - l + 1, vb(l:iter), vb(iter+1)%p, &
662 normalize = .false., overlap = hamilt(l:iter, iter, 1:psib%nst), norm = hamilt(iter + 1, iter, 1:psib%nst), &
663 gs_scheme = te%arnoldi_gs, full_batch = te%full_batch)
664
665 ! We now need to compute exp(Hm), where Hm is the projection of the linear transformation
666 ! of the Hamiltonian onto the Krylov subspace Km
667 ! See Eq. 4
668 !
669 ! Note that in the Hermitian case, we use the Lanczos algorithm that requires
670 ! only a tridiagonal matrix. Else we have an upper Hessenberg matrix.
671 do ii = 1, psib%nst
672 call zlalg_matrix_function(iter, -m_zi*deltat, hamilt(:,:,ii), expo(:,:,ii), fun, &
673 hm%is_hermitian(), tridiagonal=hm%is_hermitian())
674 res(ii) = abs(hamilt(iter + 1, iter, ii) * abs(expo(iter, 1, ii)))
675 end do !ii
676
677 ! We now estimate the error we made. This is given by the formula denoted Er2 in Sec. 5.2
678 if (all(abs(hamilt(iter + 1, iter, :)) < 1.0e4_real64*m_epsilon)) exit ! "Happy breakdown"
679 ! We normalize only if the norm is non-zero
680 ! see http://www.netlib.org/utk/people/JackDongarra/etemplates/node216.html#alg:arn0
681 norm = m_one
682 do ist = 1, psib%nst
683 if (abs(hamilt(iter + 1, iter, ist)) >= 1.0e4_real64 * m_epsilon) then
684 norm(ist) = m_one / abs(hamilt(iter + 1, iter, ist))
685 end if
686 end do
687
688 call batch_scal(mesh%np, norm, vb(iter+1)%p, a_full = .false.)
689
690 if (iter > 3 .and. all(res < te%lanczos_tol)) exit
691
692 end do !iter
693
694 if (iter == max_order) then ! Here one should consider the possibility of the happy breakdown.
695 write(message(1),'(a,i5,a,es9.2)') 'Lanczos exponential expansion did not converge after ', iter, &
696 ' iterations. Residual: ', maxval(res)
697 call messages_warning(1, namespace=namespace)
698 else
699 write(message(1),'(a,i5)') 'Debug: Lanczos exponential iterations: ', iter
700 call messages_info(1, namespace=namespace, debug_only=.true.)
701 end if
702
703 ! See Eq. 4 for the expression here
704 ! zpsi = nrm * V * expo(1:iter, 1) = nrm * V * expo * V^(T) * zpsi
705 call batch_scal(mesh%np, expo(1,1,1:psib%nst), psib, a_full = .false.)
706 ! TODO: We should have a routine batch_gemv for improved performance (see #1070 on gitlab)
707 do ii = 2, iter
708 call batch_axpy(mesh%np, beta(1:psib%nst)*expo(ii,1,1:psib%nst), vb(ii)%p, psib, a_full = .false.)
709 ! In order to apply the two exponentials, we must store the eigenvalues and eigenvectors given by zlalg_exp
710 ! And to recontruct here the exp(i*dt*H) for deltat2
711 end do
712
713 do ii = 1, max_initialized
714 call vb(ii)%p%end()
715 end do
716
717 safe_deallocate_a(vb)
718 safe_deallocate_a(hamilt)
719 safe_deallocate_a(expo)
720 safe_deallocate_a(beta)
721 safe_deallocate_a(res)
722 safe_deallocate_a(norm)
723
724 call accel_finish()
725
726 call profiling_out("EXP_LANCZOS_FUN_BATCH")
727
730
731
732 ! ---------------------------------------------------------
744 subroutine exponential_cheby_batch(te, namespace, mesh, hm, psib, chebyshev_function, op)
745 type(exponential_t), intent(inout) :: te
746 type(namespace_t), intent(in) :: namespace
747 class(mesh_t), intent(in) :: mesh
748 class(hamiltonian_abst_t), intent(inout) :: hm
749 class(batch_t), intent(inout) :: psib
750 class(chebyshev_function_t), intent(in) :: chebyshev_function
751 class(operator_t), intent(in) :: op
752
753 integer :: j, order_needed
754 complex(real64) :: coefficient
755 complex(real64), allocatable :: coefficients(:)
756 real(real64) :: error
757 class(batch_t), allocatable, target :: psi0, psi1, psi2
758 class(batch_t), pointer :: psi_n, psi_n1, psi_n2
759 integer, parameter :: max_order = 200
760
762 call profiling_in("EXP_CHEBY_BATCH")
763
764 call psib%clone_to(psi0)
765 call psib%clone_to(psi1)
766 call psib%clone_to(psi2)
767 call psib%copy_data_to(mesh%np, psi0)
768
769 order_needed = max_order
770 do j = 1, max_order
771 error = chebyshev_function%get_error(j)
772 if (error > m_zero .and. error < te%chebyshev_tol) then
773 order_needed = j
774 exit
775 end if
776 end do
777
778 call chebyshev_function%get_coefficients(j, coefficients)
779
780 ! zero-order term
781 call batch_scal(mesh%np, coefficients(0), psib)
782 ! first-order term
783 ! shifted Hamiltonian
784 call op%apply(psi0, psi1)
785 call batch_axpy(mesh%np, -hm%spectral_middle_point, psi0, psi1)
786 call batch_scal(mesh%np, m_one/hm%spectral_half_span, psi1)
787 ! accumulate result
788 call batch_axpy(mesh%np, coefficients(1), psi1, psib)
789
790 ! use pointers to avoid copies
791 psi_n => psi2
792 psi_n1 => psi1
793 psi_n2 => psi0
794
795 do j = 2, order_needed
796 ! compute shifted Hamiltonian and Chebyshev recurrence formula
797 call op%apply(psi_n1, psi_n)
798 call batch_axpy(mesh%np, -hm%spectral_middle_point, psi_n1, psi_n)
799 call batch_xpay(mesh%np, psi_n2, -m_two/hm%spectral_half_span, psi_n)
800 call batch_scal(mesh%np, -m_one, psi_n)
801
802 ! accumulate result
803 call batch_axpy(mesh%np, coefficients(j), psi_n, psib)
804
805 ! shift pointers for the three-term recurrence, this avoids copies
806 if (mod(j, 3) == 2) then
807 psi_n => psi0
808 psi_n1 => psi2
809 psi_n2 => psi1
810 else if (mod(j, 3) == 1) then
811 psi_n => psi2
812 psi_n1 => psi1
813 psi_n2 => psi0
814 else
815 psi_n => psi1
816 psi_n1 => psi0
817 psi_n2 => psi2
818 end if
819 end do
820
821 if (order_needed == max_order) then
822 write(message(1),'(a,i5,a,es9.2)') 'Chebyshev exponential expansion did not converge after ', j, &
823 ' iterations. Coefficient: ', coefficient
824 call messages_warning(1, namespace=namespace)
825 else
826 write(message(1),'(a,i5)') 'Debug: Chebyshev exponential iterations: ', j
827 call messages_info(1, namespace=namespace, debug_only=.true.)
828 end if
829
830 call psi0%end()
831 call psi1%end()
832 call psi2%end()
833 safe_deallocate_a(psi0)
834 safe_deallocate_a(psi1)
835 safe_deallocate_a(psi2)
836
837 safe_deallocate_a(coefficients)
838
839 call profiling_out("EXP_CHEBY_BATCH")
840
842 end subroutine exponential_cheby_batch
843
844 ! ---------------------------------------------------------
848 subroutine exponential_apply_all(te, namespace, gr, hm, st, deltat, order)
849 type(exponential_t), intent(inout) :: te
850 type(namespace_t), intent(in) :: namespace
851 type(grid_t), intent(inout) :: gr
852 type(hamiltonian_elec_t), intent(inout) :: hm
853 type(states_elec_t), intent(inout) :: st
854 real(real64), intent(in) :: deltat
855 integer, optional, intent(inout) :: order
856
857 integer :: ik, ib, i
858 real(real64) :: zfact
859
860 type(states_elec_t) :: st1, hst1
861
862 push_sub(exponential_apply_all)
863
864 assert(te%exp_method == exp_taylor)
865
866 call states_elec_copy(st1, st)
867 call states_elec_copy(hst1, st)
868
869 zfact = m_one
870 do i = 1, te%exp_order
871 zfact = zfact * deltat / i
872
873 if (i == 1) then
874 call zhamiltonian_elec_apply_all(hm, namespace, gr, st, hst1)
875 else
876 call zhamiltonian_elec_apply_all(hm, namespace, gr, st1, hst1)
877 end if
878
879 do ik = st%d%kpt%start, st%d%kpt%end
880 do ib = st%group%block_start, st%group%block_end
881 call batch_set_zero(st1%group%psib(ib, ik))
882 call batch_axpy(gr%np, -m_zi, hst1%group%psib(ib, ik), st1%group%psib(ib, ik))
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_set_zero(st1%group%psib(ib, ik))
920 call batch_axpy(gr%np, -m_zi, hst1%group%psib(ib, ik), st1%group%psib(ib, ik))
921 call batch_axpy(gr%np, deltat * zfact, st1%group%psib(ib, ik), st%group%psib(ib, ik))
922 end do
923 end do
924
925 end do
926
927 call states_elec_end(st1)
928 call states_elec_end(hst1)
929
930 end if
931
932 if (present(order)) order = te%exp_order*st%nik*st%nst ! This should be the correct number
933
934 pop_sub(exponential_apply_all)
935 end subroutine exponential_apply_all
936
937 subroutine exponential_apply_phi_batch(te, namespace, mesh, hm, psib, deltat, k, op)
938 class(exponential_t), intent(inout) :: te
939 type(namespace_t), intent(in) :: namespace
940 class(mesh_t), intent(in) :: mesh
941 class(hamiltonian_abst_t), intent(inout) :: hm
942 class(batch_t), intent(inout) :: psib
943 real(real64), intent(in) :: deltat
944 integer, intent(in) :: k
945 class(operator_t), target,optional, intent(in) :: op
946
947 class(chebyshev_function_t), pointer :: chebyshev_function
948 complex(real64) :: deltat_
949 class(operator_t), pointer :: op_
950
951 push_sub_with_profile(exponential_apply_phi_batch)
952
953 assert(psib%type() == type_cmplx)
954
955 if (.not. hm%is_hermitian() .and. te%exp_method == exp_chebyshev) then
956 write(message(1), '(a)') 'The Chebyshev expansion for the exponential will only converge if the imaginary'
957 write(message(2), '(a)') 'eigenvalues are small enough compared to the span of the real eigenvalues,'
958 write(message(3), '(a)') 'i.e., for ratios smaller than about 1e-3.'
959 write(message(4), '(a)') 'The Lanczos method ("TDExponentialMethod = lanczos") is guaranteed to'
960 write(message(5), '(a)') 'always converge in this case.'
961 call messages_warning(5, namespace=namespace)
962 end if
963
964 if (present(op)) then
965 op_ => op
966 else
967 op_ => hamiltonian_operator_t(namespace, mesh, hm)
968 end if
969
970 deltat_ = cmplx(deltat, m_zero, real64)
971
972 select case (te%exp_method)
973 case (exp_taylor)
974 call exponential_taylor_series_batch(te, namespace, mesh, hm, psib, deltat_, op_, phik_shift=k)
975
976 case (exp_lanczos)
977 if (k == 1) then
978 call exponential_lanczos_function_batch(te, namespace, mesh, hm, psib, deltat_, phi1, op_)
979 else if (k == 2) then
980 call exponential_lanczos_function_batch(te, namespace, mesh, hm, psib, deltat_, phi2, op_)
981 else
982 write(message(1), '(a)') 'Lanczos expansion not implemented for phi_k, k > 2'
983 call messages_fatal(1, namespace=namespace)
984 end if
985
986 case (exp_chebyshev)
987 if (k == 1) then
988 chebyshev_function => chebyshev_numerical_t(hm%spectral_half_span, hm%spectral_middle_point, deltat, phi1)
989 else if (k == 2) then
990 chebyshev_function => chebyshev_numerical_t(hm%spectral_half_span, hm%spectral_middle_point, deltat, phi2)
991 else
992 write(message(1), '(a)') 'Chebyshev expansion not implemented for phi_k, k > 2'
993 call messages_fatal(1, namespace=namespace)
994 end if
995 call exponential_cheby_batch(te, namespace, mesh, hm, psib, chebyshev_function, op_)
996 deallocate(chebyshev_function)
997 end select
998
999 if (.not.present(op)) then
1000 safe_deallocate_p(op_)
1001 end if
1002
1003 pop_sub_with_profile(exponential_apply_phi_batch)
1004 end subroutine exponential_apply_phi_batch
1005
1006 function hamiltonian_operator_constructor(namespace, mesh, hm) result(this)
1007 type(namespace_t), target, intent(in) :: namespace
1008 class(mesh_t), target, intent(in) :: mesh
1009 class(hamiltonian_abst_t), target, intent(in) :: hm
1010 type(hamiltonian_operator_t), pointer :: this
1011
1013
1014 allocate(this)
1015 call this%init(namespace, mesh, hm)
1016
1019
1020 subroutine hamiltonian_operator_init(this, namespace, mesh, hm)
1021 class(hamiltonian_operator_t), intent(inout) :: this
1022 type(namespace_t), target, intent(in) :: namespace
1023 class(mesh_t), target, intent(in) :: mesh
1024 class(hamiltonian_abst_t), target, intent(in) :: hm
1025
1027
1028 this%mesh => mesh
1029 this%namespace => namespace
1030 this%hm => hm
1031
1033 end subroutine hamiltonian_operator_init
1034
1035 subroutine hamiltonian_operator_apply(this, psib, hpsib)
1036 class(hamiltonian_operator_t), intent(in) :: this
1037 class(batch_t), intent(inout) :: psib
1038 class(batch_t), intent(inout) :: hpsib
1039
1041
1042 call this%hm%zapply(this%namespace, this%mesh, psib, hpsib)
1043
1045 end subroutine hamiltonian_operator_apply
1046
1047end module exponential_oct_m
1048
1049!! Local Variables:
1050!! mode: f90
1051!! coding: utf-8
1052!! End:
batchified version of the BLAS axpy routine:
Definition: batch_ops.F90:156
scale a batch by a constant or vector
Definition: batch_ops.F90:164
batchified version of
Definition: batch_ops.F90:172
subroutine, public accel_finish()
Definition: accel.F90:955
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
subroutine, public batch_set_zero(this, np, async)
fill all mesh functions of the batch with zero
Definition: batch_ops.F90:244
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:193
real(real64), parameter, public m_zero
Definition: global.F90:191
complex(real64), parameter, public m_zi
Definition: global.F90:205
real(real64), parameter, public m_epsilon
Definition: global.F90:207
complex(real64), parameter, public m_z1
Definition: global.F90:202
real(real64), parameter, public m_one
Definition: global.F90:192
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:2152
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:976
complex(real64) pure function, public exponential(z)
Wrapper for exponential.
Definition: math.F90:945
complex(real64) pure function, public phi1(z)
Compute phi1(z) = (exp(z)-1)/z.
Definition: math.F90:957
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:179
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:455
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), public type_cmplx
Definition: types.F90:136
Definition: xc.F90:116
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)