(git:98357aa)
Loading...
Searching...
No Matches
floquet_utils.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! **************************************************************************************************
9!> \brief Helper routines for the Floquet-Bloch band-structure calculation (floquet_main).
10!> \par History
11!> \author Shridhar Shanbhag (27.01.2026)
12! **************************************************************************************************
14 USE cell_types, ONLY: cell_type,&
21 USE cp_dbcsr_api, ONLY: dbcsr_p_type
22 USE cp_files, ONLY: close_file,&
26 USE kinds, ONLY: default_string_length,&
27 dp,&
28 int_8
32 USE kpoint_types, ONLY: get_kpoint_info,&
36 USE machine, ONLY: m_memory_details
37 USE mathconstants, ONLY: gaussi,&
38 pi,&
39 z_one,&
40 z_zero
41 USE mathlib, ONLY: geeig_right,&
46 USE physcon, ONLY: a_bohr,&
47 evolt,&
48 kelvin
52 USE qs_mo_types, ONLY: get_mo_set,&
56 USE util, ONLY: sort
57#include "./base/base_uses.f90"
58
59 IMPLICIT NONE
60
61 PRIVATE
62
63 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'floquet_utils'
64
65 ! Number of full Floquet-Hamiltonian copies (each n_f_size^2 complex(dp), 16 bytes per
66 ! element) that the subgroup must be able to hold, distributed across its ranks.
67 ! We size for 8 to leave headroom.
68 INTEGER, PARAMETER, PRIVATE :: n_fm_work_copies = 8
69
70 ! Public subroutines
71 PUBLIC :: build_floquet_matrix, &
80
81CONTAINS
82
83! **************************************************************************************************
84!> \brief ...
85!> \param qs_env ...
86!> \param xkp ...
87!> \param e_k ...
88!> \param de_dk ...
89!> \param do_parallel the option to distribute the results (e_k/de_dk) across MPI ranks.
90!> When .TRUE. k-point ikp is computed and stored only on rank MOD(ikp-1,num_pe)
91!> Default .FALSE. -> every rank computes/stores all k-points (replicated).
92! **************************************************************************************************
93 SUBROUTINE calculate_epsilon_derivative(qs_env, xkp, e_k, de_dk, do_parallel)
94 TYPE(qs_environment_type), POINTER :: qs_env
95 REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), &
96 INTENT(IN) :: xkp
97 REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :), &
98 INTENT(OUT), OPTIONAL :: e_k
99 REAL(KIND=dp), ALLOCATABLE, &
100 DIMENSION(:, :, :, :), INTENT(OUT), OPTIONAL :: de_dk
101 LOGICAL, INTENT(IN), OPTIONAL :: do_parallel
102
103 CHARACTER(LEN=*), PARAMETER :: routineN = 'calculate_epsilon_derivative'
104
105 COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: C_dH_C, C_dS_C, C_k, dH_dk_i, dS_dk_i, &
106 H_k, S_k
107 INTEGER :: handle, i_dir, ikp, ispin, mepos, n, &
108 n_img_all, n_spin, nao, nkp, num_copy, &
109 num_pe
110 INTEGER, DIMENSION(:, :), POINTER :: index_to_cell_all
111 INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index_all
112 LOGICAL :: my_do_parallel, present_dedk, present_ek
113 REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eigenvals
114 REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :, :) :: h_rs, s_rs
115 REAL(KIND=dp), DIMENSION(3, 3) :: hmat
116 TYPE(cell_type), POINTER :: cell
117 TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: matrix_ks_kp, matrix_s_kp
118 TYPE(kpoint_type), POINTER :: kpoints_all, kpoints_scf
119 TYPE(mo_set_type), DIMENSION(:), POINTER :: mos
120 TYPE(mp_para_env_type), POINTER :: para_env
121 TYPE(neighbor_list_set_p_type), DIMENSION(:), &
122 POINTER :: sab_all
123
124 CALL timeset(routinen, handle)
125
126 present_ek = PRESENT(e_k) ! calculate band energies, ε_k for all kpoints xkp
127 present_dedk = PRESENT(de_dk) ! calculate derivative, ∇_k ε_k of band energies for all kpoints xkp
128
129 IF (.NOT. (present_ek .OR. present_dedk)) cpabort("Subroutine needs either e_k or de_dk")
130
131 my_do_parallel = .false.
132 IF (PRESENT(do_parallel)) my_do_parallel = do_parallel
133
134 CALL get_qs_env(qs_env, &
135 matrix_ks_kp=matrix_ks_kp, &
136 matrix_s_kp=matrix_s_kp, &
137 sab_all=sab_all, &
138 cell=cell, &
139 kpoints=kpoints_scf, &
140 para_env=para_env, &
141 mos=mos)
142
143 CALL get_mo_set(mo_set=mos(1), nao=nao)
144 CALL get_cell(cell=cell, h=hmat)
145
146 n_spin = SIZE(matrix_ks_kp, 1)
147 nkp = SIZE(xkp, 2)
148
149 ! Distribution of k-points across ranks (mirrors qs_moment_kpoints_deep)
150 ! do_parallel = .TRUE. => ikp stored in mepos==MOD(ikp-1,num_pe)
151 ! do_parallel = .FALSE. => every rank computes all k-points (replicated)
152 mepos = 0
153 num_pe = 1
154 num_copy = nkp
155 IF (my_do_parallel) THEN
156 mepos = para_env%mepos
157 num_pe = para_env%num_pe
158 num_copy = ceiling(real(nkp)/num_pe)
159 END IF
160
161 ! create kpoint environment kpoints_all which contains all neighbor cells R
162 ! without considering any lattice symmetry
163 NULLIFY (kpoints_all)
164 CALL kpoint_create(kpoints_all)
165 CALL kpoint_init_cell_index(kpoints_all, sab_all, para_env, n_img_all)
166 CALL get_kpoint_info(kpoints_all, cell_to_index=cell_to_index_all, index_to_cell=index_to_cell_all)
167
168 ALLOCATE (s_rs(1, nao, nao, n_img_all), h_rs(n_spin, nao, nao, n_img_all), source=0.0_dp)
169
170 ! Convert real-space dbcsr matrices into arrays
171 CALL replicate_rs_matrices(matrix_s_kp, kpoints_scf, s_rs, cell_to_index_all)
172 CALL replicate_rs_matrices(matrix_ks_kp, kpoints_scf, h_rs, cell_to_index_all)
173
174 IF (present_dedk) ALLOCATE (de_dk(n_spin, num_copy, 3, nao), source=0.0_dp)
175 IF (present_ek) ALLOCATE (e_k(n_spin, num_copy, nao), source=0.0_dp)
176
177!$OMP PARALLEL DEFAULT(NONE) &
178!$OMP PRIVATE(ikp, ispin, S_k, H_k, eigenvals, C_k, dS_dk_i, dH_dk_i, C_dS_C, C_dH_C) &
179!$OMP SHARED(nao, n_spin, de_dk, e_k, present_ek, present_dedk, mepos, num_pe, &
180!$OMP nkp, xkp, S_rs, H_rs, index_to_cell_all, hmat)
181 IF (present_dedk) ALLOCATE (ds_dk_i(nao, nao), c_ds_c(nao, nao), &
182 dh_dk_i(nao, nao), c_dh_c(nao, nao), source=z_zero)
183 ALLOCATE (c_k(nao, nao), s_k(nao, nao), h_k(nao, nao), source=z_zero)
184 ALLOCATE (eigenvals(nao), source=0.0_dp)
185!$OMP DO COLLAPSE(2)
186 DO ispin = 1, n_spin
187 DO ikp = 1, nkp
188 IF (mod(ikp - 1, num_pe) /= mepos) cycle
189
190 ! S^R -> S(k), H^R -> H(k)
191 s_k = 0
192 h_k = 0
193 CALL rs_to_kp(s_rs(1, :, :, :), s_k, index_to_cell_all, xkp(:, ikp))
194 CALL rs_to_kp(h_rs(ispin, :, :, :), h_k, index_to_cell_all, xkp(:, ikp))
195
196 ! Diagonalize H(k)C(k) = S(k)C(k)ε(k)
197 CALL geeig_right(h_k, s_k, eigenvals, c_k)
198 IF (present_ek) e_k(ispin, ceiling(real(ikp)/num_pe), :) = eigenvals(:)
199
200 IF (present_dedk) THEN
201 ! Evaluate the derivatives using
202 ! ∇ ε_k = C^H(k) ∇ H_k C(k) - ε_k C^H(k) ∇ S_k C(k)
203 DO i_dir = 1, 3
204 CALL rs_to_kp(s_rs(1, :, :, :), ds_dk_i, index_to_cell_all, xkp(:, ikp), i_dir, hmat)
205 CALL rs_to_kp(h_rs(ispin, :, :, :), dh_dk_i, index_to_cell_all, xkp(:, ikp), i_dir, hmat)
206
207 CALL gemm_square(c_k, 'C', ds_dk_i, 'N', c_k, 'N', c_ds_c)
208 CALL gemm_square(c_k, 'C', dh_dk_i, 'N', c_k, 'N', c_dh_c)
209
210 DO n = 1, nao
211 de_dk(ispin, ceiling(real(ikp)/num_pe), i_dir, n) = &
212 dble(c_dh_c(n, n)) - dble(eigenvals(n)*c_ds_c(n, n))
213 END DO
214 END DO
215 END IF
216 END DO
217 END DO
218!$OMP END DO
219 IF (present_dedk) DEALLOCATE (ds_dk_i, c_ds_c, dh_dk_i, c_dh_c)
220 DEALLOCATE (s_k, h_k, c_k, eigenvals)
221!$OMP END PARALLEL
222 DEALLOCATE (s_rs, h_rs)
223 CALL kpoint_release(kpoints_all)
224
225 CALL timestop(handle)
226
227 END SUBROUTINE calculate_epsilon_derivative
228
229! **************************************************************************************************
230!> \brief Momentum matrix elements p_nm(k) for one k-point and one spin.
231!> \param e_k_kp_spin ...
232!> \param de_dk_kp_spin ...
233!> \param dipole_kp_spin ...
234!> \param momentum ...
235! **************************************************************************************************
236 SUBROUTINE build_momentum_matrix(e_k_kp_spin, de_dk_kp_spin, dipole_kp_spin, momentum)
237 REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: e_k_kp_spin
238 REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: de_dk_kp_spin
239 COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: dipole_kp_spin
240 COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(OUT) :: momentum
241
242 CHARACTER(LEN=*), PARAMETER :: routineN = 'build_momentum_matrix'
243
244 INTEGER :: handle, i, i_dir, j, nao
245
246 CALL timeset(routinen, handle)
247
248 nao = SIZE(e_k_kp_spin)
249
250 ! We calculate momentum matrix elements p_nm = <ψ_n|-iħ∇_r|ψ_m>
251 ! p_nm = <u_n|(ħk - iħ∇_r)|u_m>
252 ! p_nm = i d_nm (ε_n - ε_m) + ∇_k ε_n δ_nm
253
254!$OMP PARALLEL DEFAULT(NONE) &
255!$OMP PRIVATE(i_dir, i, j) &
256!$OMP SHARED(nao, momentum, dipole_kp_spin, e_k_kp_spin, de_dk_kp_spin)
257!$OMP DO COLLAPSE(3)
258 DO i_dir = 1, 3
259 DO i = 1, nao
260 DO j = 1, nao
261 IF (j == i) THEN
262 momentum(i_dir, i, j) = de_dk_kp_spin(i_dir, i)
263 ELSE
264 momentum(i_dir, i, j) = gaussi*(e_k_kp_spin(i) - e_k_kp_spin(j))* &
265 dipole_kp_spin(i_dir, i, j)
266 END IF
267 END DO
268 END DO
269 END DO
270!$OMP END DO
271!$OMP END PARALLEL
272
273 CALL timestop(handle)
274 END SUBROUTINE build_momentum_matrix
275
276! **************************************************************************************************
277!> \brief Off-diagonal coupling block of H_F(k) for one k-point and spin, from precomputed data.
278!> \param bs_env ...
279!> \param e_k_kp_spin ...
280!> \param de_dk_kp_spin ...
281!> \param dipole_kp_spin ...
282!> \param off_diag_m ...
283! **************************************************************************************************
284 SUBROUTINE build_off_diagonal_matrix(bs_env, e_k_kp_spin, de_dk_kp_spin, dipole_kp_spin, off_diag_m)
285 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
286 REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: e_k_kp_spin
287 REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: de_dk_kp_spin
288 COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: dipole_kp_spin
289 COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: off_diag_m
290
291 CHARACTER(LEN=*), PARAMETER :: routineN = 'build_off_diagonal_matrix'
292
293 COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: momentum
294 COMPLEX(KIND=dp), DIMENSION(3) :: efactor
295 INTEGER :: handle, i, i_dir, j, nao
296 REAL(KIND=dp) :: amplitude, omega
297 REAL(KIND=dp), DIMENSION(3) :: e_vec, phi, polarisation
298
299 CALL timeset(routinen, handle)
300
301 ! Builds the matrix that occupies the off diagonal blocks in the Floquet Matrix H_F
302
303 nao = SIZE(e_k_kp_spin)
304
305 polarisation(:) = bs_env%floquet_polarisation(:)
306 IF (sqrt(sum(polarisation**2)) < epsilon(0.0_dp)) THEN
307 cpabort("Invalid (too small) polarisation vector specified for POLARISATION")
308 END IF
309
310 amplitude = bs_env%floquet_amplitude
311 e_vec(:) = amplitude*polarisation
312 phi(:) = pi*bs_env%floquet_phi(:)
313 omega = bs_env%floquet_omega
314
315 ALLOCATE (momentum(3, nao, nao), source=z_zero)
316 CALL build_momentum_matrix(e_k_kp_spin, de_dk_kp_spin, dipole_kp_spin, momentum)
317
318 ! E_factor(α) = (i E(α) exp(i·φ(α)))/(2ω), where α = x,y,z
319 DO i_dir = 1, 3
320 efactor(i_dir) = gaussi*e_vec(i_dir)*cmplx(cos(phi(i_dir)), sin(phi(i_dir)), kind=dp)/(2*omega)
321 END DO
322
323 ! off_diag_uv = Σ_α [p_uv^α(k) · E_factor(α)], where α = x,y,z
324 off_diag_m(:, :) = z_zero
325 DO i_dir = 1, 3
326 DO i = 1, nao
327 DO j = 1, nao
328 off_diag_m(i, j) = off_diag_m(i, j) + &
329 momentum(i_dir, i, j)*efactor(i_dir)
330 END DO
331 END DO
332 END DO
333 DEALLOCATE (momentum)
334
335 CALL timestop(handle)
336
337 END SUBROUTINE build_off_diagonal_matrix
338
339! **************************************************************************************************
340!> \brief Diagonal block of H_F(k) for Floquet sector f_index.
341!> \param bs_env ...
342!> \param e_k_kp_spin ...
343!> \param f_index Floquet sector index
344!> \param diag_e ...
345! **************************************************************************************************
346 SUBROUTINE build_diagonal_matrix(bs_env, e_k_kp_spin, f_index, diag_e)
347 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
348 REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: e_k_kp_spin
349 INTEGER, INTENT(IN) :: f_index
350 COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: diag_e
351
352 CHARACTER(LEN=*), PARAMETER :: routineN = 'build_diagonal_matrix'
353
354 INTEGER :: handle, i, nao
355 REAL(KIND=dp) :: omega
356
357 CALL timeset(routinen, handle)
358 ! Builds the diagonal block of the H_F for Floquet sector index f_index
359 ! Equilibrium band energies shifted by f_index·ħΩ, i.e., diag_e(n,n) = ε_{nk} + f_index·ħΩ.
360 nao = SIZE(e_k_kp_spin)
361 omega = bs_env%floquet_omega
362 diag_e(:, :) = z_zero
363 DO i = 1, nao
364 diag_e(i, i) = e_k_kp_spin(i) + f_index*omega
365 END DO
366
367 CALL timestop(handle)
368
369 END SUBROUTINE build_diagonal_matrix
370
371! **************************************************************************************************
372!> \brief Builds the Floquet-Bloch Hamiltonian H_F(k) for a single k-point and spin channel from
373!> the band data currently held in floquet_env.
374!> \param bs_env ...
375!> \param floquet_env ...
376!> \param floquet_matrix ...
377! **************************************************************************************************
378 SUBROUTINE build_floquet_matrix(bs_env, floquet_env, floquet_matrix)
379 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
380 TYPE(floquet_env_type), INTENT(IN) :: floquet_env
381 TYPE(cp_cfm_type), INTENT(IN) :: floquet_matrix
382
383 CHARACTER(LEN=*), PARAMETER :: routinen = 'build_floquet_matrix'
384
385 COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: conj_off_diag_m, diag_e, off_diag_m
386 INTEGER :: f_index, handle, i, i_f, max_f_index, &
387 n_fbands, nao
388
389 CALL timeset(routinen, handle)
390
391 nao = floquet_env%nao
392 max_f_index = floquet_env%max_f_index
393 n_fbands = 1 + 2*max_f_index
394
395 ! Creates the floquet matrix H_F by placing the diagonal and off diagonal blocks
396 ALLOCATE (diag_e(nao, nao), source=z_zero)
397 ALLOCATE (off_diag_m(nao, nao), source=z_zero)
398 ALLOCATE (conj_off_diag_m(nao, nao), source=z_zero)
399
400 CALL build_off_diagonal_matrix(bs_env, floquet_env%e_k_kp_spin, floquet_env%de_dk_kp_spin, &
401 floquet_env%dipole_kp_spin, off_diag_m)
402 conj_off_diag_m(:, :) = conjg(transpose(off_diag_m(:, :)))
403
404 floquet_matrix%local_data(:, :) = z_zero
405 DO i = 1, n_fbands
406 i_f = 1 + (i - 1)*nao
407 f_index = i - max_f_index - 1
408 CALL build_diagonal_matrix(bs_env, floquet_env%e_k_kp_spin, f_index, diag_e)
409 CALL cp_cfm_set_submatrix(floquet_matrix, diag_e, i_f, i_f)
410 IF (i > 1) THEN
411 CALL cp_cfm_set_submatrix(floquet_matrix, off_diag_m, i_f, i_f - nao)
412 CALL cp_cfm_set_submatrix(floquet_matrix, conj_off_diag_m, i_f - nao, i_f)
413 END IF
414 END DO
415 DEALLOCATE (diag_e, off_diag_m, conj_off_diag_m)
416
417 CALL timestop(handle)
418 END SUBROUTINE build_floquet_matrix
419
420! **************************************************************************************************
421!> \brief Make this k-point/spin's band data available to every rank of the owning subgroup.
422!> \param para_env the global parallel environment
423!> \param para_env_sub the subgroup parallel environment
424!> \param ispin the spin channel
425!> \param ikp the DOS k-point index
426!> \param e_k band energies for all DOS k-points, distributed
427!> \param de_dk band-energy k-derivatives, distributed
428!> \param dipole dipole matrix elements, distributed
429!> \param floquet_env ...
430! **************************************************************************************************
431 SUBROUTINE distribute_floquet_kp_data(para_env, para_env_sub, ispin, ikp, e_k, de_dk, dipole, &
432 floquet_env)
433 TYPE(mp_para_env_type), POINTER :: para_env, para_env_sub
434 INTEGER, INTENT(IN) :: ispin, ikp
435 REAL(kind=dp), DIMENSION(:, :, :), INTENT(IN) :: e_k
436 REAL(kind=dp), DIMENSION(:, :, :, :), INTENT(IN) :: de_dk
437 COMPLEX(KIND=dp), DIMENSION(:, :, :, :, :), &
438 INTENT(IN) :: dipole
439 TYPE(floquet_env_type), INTENT(INOUT) :: floquet_env
440
441 CHARACTER(LEN=*), PARAMETER :: routinen = 'distribute_floquet_kp_data'
442
443 INTEGER :: handle, loc_idx, local_src, owner
444
445 CALL timeset(routinen, handle)
446
447 ! Identify the owner's rank within the subgroup (all subgroup ranks agree on local_src).
448 owner = mod(ikp - 1, para_env%num_pe)
449
450 local_src = -1
451 IF (para_env%mepos == owner) local_src = para_env_sub%mepos
452 CALL para_env_sub%max(local_src)
453
454 ! The owner copies its single-spin slice of this k-point's distributed band data into the
455 ! broadcast buffers, then broadcasts them to the whole subgroup.
456 IF (para_env%mepos == owner) THEN
457 loc_idx = ceiling(real(ikp)/para_env%num_pe)
458 floquet_env%e_k_kp_spin(:) = e_k(ispin, loc_idx, :)
459 floquet_env%de_dk_kp_spin(:, :) = de_dk(ispin, loc_idx, :, :)
460 floquet_env%dipole_kp_spin(:, :, :) = dipole(ispin, loc_idx, :, :, :)
461 END IF
462 CALL para_env_sub%bcast(floquet_env%e_k_kp_spin, local_src)
463 CALL para_env_sub%bcast(floquet_env%de_dk_kp_spin, local_src)
464 CALL para_env_sub%bcast(floquet_env%dipole_kp_spin, local_src)
465
466 CALL timestop(handle)
467
468 END SUBROUTINE distribute_floquet_kp_data
469
470! **************************************************************************************************
471!> \brief Precompute, on the global communicator, the band quantities needed to assemble the
472!> Floquet-Bloch Hamiltonian for all DOS k-points (all spins), distributed across ranks.
473!> \param qs_env ...
474!> \param bs_env ...
475!> \param e_k ...
476!> \param de_dk ...
477!> \param dipole ...
478! **************************************************************************************************
479 SUBROUTINE compute_e_k_de_dk_dipole(qs_env, bs_env, e_k, de_dk, dipole)
480 TYPE(qs_environment_type), POINTER :: qs_env
481 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
482 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :), &
483 INTENT(OUT) :: e_k
484 REAL(kind=dp), ALLOCATABLE, &
485 DIMENSION(:, :, :, :), INTENT(OUT) :: de_dk
486 COMPLEX(KIND=dp), ALLOCATABLE, &
487 DIMENSION(:, :, :, :, :), INTENT(OUT) :: dipole
488
489 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_e_k_de_dk_dipole'
490
491 INTEGER :: handle, ikp, nkp_only_bs, nkp_start
492 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: xkp_all
493
494 CALL timeset(routinen, handle)
495
496 ! The band-structure k-points are the last nkp_only_bs entries of kpoints_DOS%xkp, stored
497 ! after the nkp_only_DOS DOS-only k-points.
498 nkp_start = bs_env%nkp_only_DOS
499 nkp_only_bs = bs_env%nkp_only_bs
500
501 ! Collect the coordinates of all band-structure k-points (global index nkp_start + local).
502 ALLOCATE (xkp_all(3, nkp_only_bs))
503 DO ikp = 1, nkp_only_bs
504 xkp_all(:, ikp) = bs_env%kpoints_DOS%xkp(1:3, nkp_start + ikp)
505 END DO
506
507 ! Calculate and distribute the results across ranks
508 ! One k-point per rank, round-robin by global rank
509 ! So the results for ikp are stored in mepos==MOD(ikp-1,num_pe)
510 CALL calculate_epsilon_derivative(qs_env, xkp_all, e_k=e_k, de_dk=de_dk, do_parallel=.true.)
511 CALL qs_moment_kpoints_deep(qs_env, xkp_all, dipole, do_parallel=.true.)
512
513 DEALLOCATE (xkp_all)
514
515 CALL timestop(handle)
516 END SUBROUTINE compute_e_k_de_dk_dipole
517
518! **************************************************************************************************
519!> \brief Finds the number of MPI ranks that share a physical node, determined by splitting the
520!> given communicator into node-local communicators via MPI's shared-memory split type.
521!> \param para_env ...
522!> \return the number of ranks on the calling rank's node
523! **************************************************************************************************
524 FUNCTION find_ranks_per_node(para_env) RESULT(ranks_per_node)
525 TYPE(mp_para_env_type), POINTER :: para_env
526 INTEGER :: ranks_per_node
527
528 TYPE(mp_comm_type) :: node_comm
529
530 CALL node_comm%from_split_type(para_env, mp_comm_split_type_shared, key=para_env%mepos)
531 ranks_per_node = max(1, node_comm%num_pe)
532 CALL node_comm%free()
533
534 END FUNCTION find_ranks_per_node
535
536! **************************************************************************************************
537!> \brief Choose the optimal number of MPI ranks per subgroup for Floquet calculations.
538!> \param qs_env ...
539!> \param bs_env ...
540!> \return the chosen number of ranks per subgroup (1 .. num_pe)
541! **************************************************************************************************
542 FUNCTION floquet_determine_subgroup_size(qs_env, bs_env) RESULT(group_size)
543 TYPE(qs_environment_type), POINTER :: qs_env
544 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
545 INTEGER :: group_size
546
547 CHARACTER(LEN=*), PARAMETER :: routinen = 'floquet_determine_subgroup_size'
548
549 INTEGER :: g_load, g_mem, handle, n_f_size, nao, &
550 nkp_only_bs, ranks_per_node, unit_nr
551 INTEGER(KIND=int_8) :: buffers, cached, memfree, memlikelyfree, &
552 memtotal, needed_bytes, slab, &
553 sreclaimable, usable_per_rank
554 REAL(kind=dp) :: mem_fill_fraction
555 TYPE(mo_set_type), DIMENSION(:), POINTER :: mos
556 TYPE(mp_para_env_type), POINTER :: para_env
557
558 CALL timeset(routinen, handle)
559
560 CALL get_qs_env(qs_env, para_env=para_env, mos=mos)
561 CALL get_mo_set(mo_set=mos(1), nao=nao)
562
563 unit_nr = bs_env%unit_nr
564 n_f_size = nao*(1 + 2*bs_env%max_floquet_index)
565 nkp_only_bs = bs_env%nkp_only_bs
566 mem_fill_fraction = bs_env%floquet_mem_fill_fraction
567
568 ! We choose the subgroup size to be the larger of two floors:
569 ! 1. Memory floor G_mem: the subgroup must jointly hold the Floquet working set,
570 ! G_mem = ceil(needed_bytes/usable per-rank memory), where the usable memory is
571 ! the mem_fill_fraction of the per-rank FREE memory.
572 ! 2. K-point floor G_load = floor(num_pe / nkp_only_bs): the LARGEST G that still
573 ! yields at least nkp_only_bs subgroups (floor(num_pe/G) >= nkp_only_bs). When ranks
574 ! outnumber k-points this hands every k-point its own (larger, hence faster)
575 ! subgroup instead of leaving spare ranks idle.
576
577 ! usable_per_rank = a fraction of the per-rank FREE memory, not including SCF and other data
578 CALL m_memory_details(memtotal, memfree, buffers, cached, slab, sreclaimable, memlikelyfree)
579 ranks_per_node = find_ranks_per_node(para_env)
580 usable_per_rank = int(mem_fill_fraction*real(memfree, dp), int_8)/int(ranks_per_node, int_8)
581
582 ! Total memory the subgroup must hold: n_fm_work_copies copies of the Floquet
583 ! Hamiltonian, each n_f_size^2 complex(dp) numbers at 16 bytes each.
584 needed_bytes = 16_int_8*int(n_fm_work_copies, int_8)*int(n_f_size, int_8)**2
585
586 IF (usable_per_rank <= 0_int_8) THEN
587 ! Memory info unavailable (e.g. no /proc found) use one subgroup spanning all ranks
588 g_mem = para_env%num_pe
589 ELSE
590 ! Memory floor: smallest G with needed_bytes/G <= usable_per_rank
591 g_mem = int(min((needed_bytes + usable_per_rank - 1_int_8)/usable_per_rank, &
592 int(para_env%num_pe, int_8)))
593 END IF
594
595 IF (g_mem > para_env%num_pe) cpwarn("Total Memory Likely Insufficient, process may be killed")
596
597 ! K-point floor: floor(num_pe / nkp_only_bs) = largest G that gives at least nkp_only_bs subgroups
598 g_load = para_env%num_pe/max(nkp_only_bs, 1)
599
600 group_size = max(g_mem, g_load)
601 group_size = min(group_size, para_env%num_pe)
602 group_size = max(group_size, 1)
603 CALL para_env%max(group_size)
604
605 IF (unit_nr > 0) THEN
606 WRITE (unit_nr, '(T2,A)') ""
607 WRITE (unit_nr, '(T2,A,T66,I15)') "FLOQUET MEMORY | Detected MPI ranks per node:", ranks_per_node
608 WRITE (unit_nr, '(T2,A,T66,I12,A)') "FLOQUET MEMORY | Free memory per rank:", &
609 (memlikelyfree/int(ranks_per_node, int_8))/1048576_int_8, " MB"
610 WRITE (unit_nr, '(T2,A,T66,I12,A)') "FLOQUET MEMORY | Usable per rank (reserve applied):", &
611 usable_per_rank/1048576_int_8, " MB"
612 WRITE (unit_nr, '(T2,A,T66,I15)') "FLOQUET MEMORY | Floquet copies needed:", n_fm_work_copies
613 WRITE (unit_nr, '(T2,A,T66,I12,A)') "FLOQUET MEMORY | Total Floquet working set:", &
614 needed_bytes/1048576_int_8, " MB"
615 WRITE (unit_nr, '(T2,A,T66,I12,A)') "FLOQUET MEMORY | Floquet working set per rank:", &
616 (needed_bytes/int(max(group_size, 1), int_8))/1048576_int_8, " MB"
617 WRITE (unit_nr, '(T2,A,T66,I15)') "FLOQUET MEMORY | Memory floor (ranks/subgroup):", g_mem
618 WRITE (unit_nr, '(T2,A,T66,I15)') "FLOQUET MEMORY | K-point floor (ranks/subgroup):", g_load
619 WRITE (unit_nr, '(T2,A,T66,I15)') "FLOQUET MEMORY | Chosen MPI ranks per subgroup:", group_size
620 END IF
621
622 CALL timestop(handle)
623 END FUNCTION floquet_determine_subgroup_size
624
625! **************************************************************************************************
626!> \brief Split the para_env into subgroups so that the Floquet Hamiltonian is distributed across
627!> the ranks of each subgroup. Also creates the subgroup BLACS context.
628!> \param qs_env ...
629!> \param bs_env ...
630!> \param para_env_sub the created subgroup parallel environment
631!> \param blacs_env_sub the created subgroup BLACS context
632!> \param group_distribution subgroup index of every global rank, 0:num_pe-1
633!> \param ngroups the number of subgroups created
634! **************************************************************************************************
635 SUBROUTINE make_floquet_subgroups(qs_env, bs_env, para_env_sub, blacs_env_sub, &
636 group_distribution, ngroups)
637 TYPE(qs_environment_type), POINTER :: qs_env
638 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
639 TYPE(mp_para_env_type), POINTER :: para_env_sub
640 TYPE(cp_blacs_env_type), POINTER :: blacs_env_sub
641 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: group_distribution
642 INTEGER, INTENT(OUT) :: ngroups
643
644 CHARACTER(LEN=*), PARAMETER :: routinen = 'make_floquet_subgroups'
645
646 INTEGER :: group_size, handle, n_spin, nkp_only_bs, &
647 stride_kp, unit_nr
648 TYPE(mp_para_env_type), POINTER :: para_env
649
650 CALL timeset(routinen, handle)
651
652 CALL get_qs_env(qs_env, para_env=para_env)
653 unit_nr = bs_env%unit_nr
654 nkp_only_bs = bs_env%nkp_only_bs
655 n_spin = bs_env%n_spin
656
657 ! We split the global communicator into subgroups with the Floquet Hamiltonian distributed
658 ! across the processes in each subgroup and the k-points distributed across subgroups.
659 group_size = floquet_determine_subgroup_size(qs_env, bs_env)
660
661 IF (nkp_only_bs < para_env%num_pe) THEN
662 stride_kp = para_env%num_pe/group_size
663 ELSE
664 stride_kp = 1
665 END IF
666 ALLOCATE (group_distribution(0:para_env%num_pe - 1))
667 ALLOCATE (para_env_sub)
668 CALL para_env_sub%from_split(comm=para_env, ngroups=ngroups, &
669 group_distribution=group_distribution, &
670 subgroup_min_size=group_size, stride=stride_kp)
671 IF (unit_nr > 0) THEN
672 WRITE (unit_nr, '(T2,A,T66,I15)') "FLOQUET MEMORY | Subgroup rank stride:", stride_kp
673 WRITE (unit_nr, '(T2,A,T66,I15)') "FLOQUET MEMORY | Number of subgroups:", ngroups
674 WRITE (unit_nr, '(/,T2,A,I5,A,I1,A)') "FLOQUET CALCULATIONS PROGRESS OUT OF", nkp_only_bs, &
675 " K-POINTS AND ", n_spin, " SPINS"
676 END IF
677
678 NULLIFY (blacs_env_sub)
679 CALL cp_blacs_env_create(blacs_env_sub, para_env_sub)
680
681 CALL timestop(handle)
682
683 END SUBROUTINE make_floquet_subgroups
684
685! **************************************************************************************************
686!> \brief Central and boundary-sector weights of every Floquet eigenvector, stored into
687!> floquet_env%w0 and floquet_env%wE.
688!> \param floquet_env ...
689!> \param cfm_eigenvectors Floquet eigenvectors
690! **************************************************************************************************
691 SUBROUTINE floquet_sector_weights(floquet_env, cfm_eigenvectors)
692 TYPE(floquet_env_type), INTENT(INOUT) :: floquet_env
693 TYPE(cp_cfm_type), INTENT(IN) :: cfm_eigenvectors
694
695 CHARACTER(LEN=*), PARAMETER :: routinen = 'floquet_sector_weights'
696
697 COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: v_block
698 INTEGER :: handle, max_f_index, n_f_size, nao
699
700 CALL timeset(routinen, handle)
701
702 nao = floquet_env%nao
703 max_f_index = floquet_env%max_f_index
704 n_f_size = floquet_env%n_f_size
705
706 ALLOCATE (v_block(nao, n_f_size))
707
708 ! Evaluate w0_α = Σ_{n=1..nao} |<n,m=0|α>|^2 (central sector)
709 CALL cp_cfm_get_submatrix(cfm_eigenvectors, v_block, 1 + nao*max_f_index, 1)
710 floquet_env%w0(:) = sum(abs(v_block)**2, dim=1)
711
712 ! Evaluate wE_α = Σ_{n=1..nao} (|<n,m=-M|α>|^2 + |<n,m=+M|α>|^2) (outermost rungs)
713 floquet_env%wE(:) = 0.0_dp
714 IF (max_f_index >= 1) THEN
715 CALL cp_cfm_get_submatrix(cfm_eigenvectors, v_block, 1, 1)
716 floquet_env%wE(:) = sum(abs(v_block)**2, dim=1)
717
718 CALL cp_cfm_get_submatrix(cfm_eigenvectors, v_block, 1 + nao*2*max_f_index, 1)
719 floquet_env%wE(:) = floquet_env%wE(:) + sum(abs(v_block)**2, dim=1)
720 END IF
721
722 DEALLOCATE (v_block)
723
724 ! The orthonormal rows of a unitary matrix are such that Σ_α w0_α = nao.
725 cpassert(abs(sum(floquet_env%w0) - real(nao, dp)) < 1.0e-6_dp*real(nao, dp))
726
727 CALL timestop(handle)
728
729 END SUBROUTINE floquet_sector_weights
730
731! **************************************************************************************************
732!> \brief Checks that MAX_FLOQUET_INDEX is large enough and the Floquet Hamiltonian was
733!> truncated far enough away from the central sector to leave it unperturbed.
734!> \param bs_env ...
735!> \param floquet_env holds the central- (w0) and outermost-sector (wE) weights
736! **************************************************************************************************
737 SUBROUTINE check_floquet_convergence(bs_env, floquet_env)
738 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
739 TYPE(floquet_env_type), INTENT(IN) :: floquet_env
740
741 CHARACTER(LEN=*), PARAMETER :: routinen = 'check_floquet_convergence'
742
743 CHARACTER(LEN=default_string_length) :: msg
744 INTEGER :: handle
745 REAL(kind=dp) :: leak
746
747 ! The truncation is converged if m=0 states have small weight on outermost rungs m = ±M
748 CALL timeset(routinen, handle)
749
750 ! MAX_FLOQUET_INDEX = 0 means no sidebands at all
751 IF (bs_env%max_floquet_index < 1) THEN
752 CALL timestop(handle)
753 RETURN
754 END IF
755
756 ! No cleanly m=0-dominated state exists: the drive has hybridised every band with
757 ! its sidebands, strong field but not a truncation failure.
758 IF (.NOT. any(floquet_env%w0 > 0.5_dp)) THEN
759 cpwarn("Floquet: no m=0-dominated state; cannot assess MAX_FLOQUET_INDEX convergence.")
760 CALL timestop(handle)
761 RETURN
762 END IF
763
764 leak = maxval(floquet_env%wE, mask=(floquet_env%w0 > 0.5_dp))
765
766 IF (bs_env%eps_floquet > 0.0_dp .AND. leak > bs_env%eps_floquet) THEN
767 WRITE (msg, '(A,ES10.2E2,A,ES10.2E2)') &
768 "MAX_FLOQUET_INDEX is too small. Leak: ", leak, "exceeds EPS_FLOQUET: ", bs_env%eps_floquet
769 cpabort(trim(msg))
770 END IF
771
772 CALL timestop(handle)
773
774 END SUBROUTINE check_floquet_convergence
775
776! **************************************************************************************************
777!> \brief Energy origin for the Floquet calculations, chosen to be the valence band maximum.
778!> \param bs_env ...
779!> \return the valence band maximum
780! **************************************************************************************************
781 FUNCTION floquet_reference_energy(bs_env) RESULT(mu)
782 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
783 REAL(kind=dp) :: mu
784
785 IF (bs_env%do_gw .OR. &
786 bs_env%small_cell_full_kp_or_large_cell_Gamma == small_cell_full_kp) THEN
787 mu = bs_env%band_edges_scf%VBM
788 ELSE IF (bs_env%n_spin == 1) THEN
789 mu = bs_env%eigenval_scf_Gamma(bs_env%n_occ(1), 1)
790 ELSE
791 mu = max(bs_env%eigenval_scf_Gamma(bs_env%n_occ(1), 1), &
792 bs_env%eigenval_scf_Gamma(bs_env%n_occ(2), 2))
793 END IF
794
795 END FUNCTION floquet_reference_energy
796
797! **************************************************************************************************
798!> \brief Calculate all Floquet observables for one k-point and spin: the DOS (a_k), the m=0 bands,
799!> the quasi-energies, and store in floquet_env.
800!> \param bs_env ...
801!> \param para_env_sub ...
802!> \param ispin ...
803!> \param ikp ...
804!> \param floquet_env holds eigenvalues, w0 (input) and a_k, m0_energies, m0_weights,
805!> quasi_energies (output)
806! **************************************************************************************************
807 SUBROUTINE calculate_floquet_observables(bs_env, para_env_sub, ispin, ikp, floquet_env)
808 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
809 TYPE(mp_para_env_type), POINTER :: para_env_sub
810 INTEGER, INTENT(IN) :: ispin, ikp
811 TYPE(floquet_env_type), INTENT(INOUT) :: floquet_env
812
813 CHARACTER(LEN=*), PARAMETER :: routinen = 'calculate_floquet_observables'
814
815 INTEGER :: handle, i, i_e, j, n_e, n_f_size, nao
816 INTEGER, ALLOCATABLE, DIMENSION(:) :: work
817 REAL(kind=dp) :: broad, cum, e_min, energy, energy_step, &
818 mu, omega, target_level
819
820 CALL timeset(routinen, handle)
821
822 n_f_size = floquet_env%n_f_size
823 nao = floquet_env%nao
824 n_e = floquet_env%n_E
825
826 mu = floquet_reference_energy(bs_env)
827 omega = bs_env%floquet_omega
828 broad = bs_env%broadening_floquet
829 energy_step = bs_env%energy_step_floquet
830 e_min = mu - bs_env%energy_window_floquet
831
832 ! (1) Spectral function / DOS, A(k,E) = -(1/π) Im Tr_KS G^R_00(k,E),
833 ! where G^R_00(k,E) = (E + iη - H_F)^(-1) is the retarded Green's function
834 ! With H_F = Σ_α λ_α |α><α|, we simplify
835 ! A(k,E) = -(1/π) Σ_α w0_α Im[1/(E + iη - λ_α)], η = broadening/2.
836 ! This avoids the per-energy dense inversion of the full H_F.
837
838 DO i_e = 1, n_e
839 energy = e_min + i_e*energy_step
840 floquet_env%a_k(i_e) = -sum(floquet_env%w0(:)* &
841 aimag(z_one/(energy + gaussi*broad/2.0_dp - &
842 floquet_env%eigenvalues(:))))/pi
843 END DO
844
845 ! (2) m=0 band structure by weighted count
846 cum = 0.0_dp
847 j = 0
848 DO i = 1, nao
849 target_level = real(i, dp) - 0.5_dp
850 DO WHILE (cum < target_level .AND. j < n_f_size)
851 j = j + 1
852 cum = cum + floquet_env%w0(j)
853 END DO
854 floquet_env%m0_energies(i) = floquet_env%eigenvalues(j) - mu
855 floquet_env%m0_weights(i) = floquet_env%w0(j)
856 END DO
857
858 ! (3) Fold the m=0 bands (already relative to the VBM) into the first Floquet Brillouin zone.
859 floquet_env%quasi_energies(:) = floquet_env%m0_energies &
860 - omega*real(ceiling(floquet_env%m0_energies/omega - 0.5_dp), dp)
861
862 ! Sort the quasi-energies
863 ALLOCATE (work(nao))
864 CALL sort(floquet_env%quasi_energies, nao, work)
865 DEALLOCATE (work)
866
867 ! Store this result on the subgroup source only, so the global sum below picks up
868 ! each work item only once
869 IF (para_env_sub%is_source()) THEN
870 floquet_env%all_quasi_energies(:, ispin, ikp) = floquet_env%quasi_energies(:)
871 floquet_env%all_a_k(:, ispin, ikp) = floquet_env%a_k(:)
872 floquet_env%all_m0_energies(:, ispin, ikp) = floquet_env%m0_energies(:)
873 floquet_env%all_m0_weights(:, ispin, ikp) = floquet_env%m0_weights(:)
874 END IF
875
876 CALL timestop(handle)
877
878 END SUBROUTINE calculate_floquet_observables
879
880! **************************************************************************************************
881!> \brief Print the Floquet header with the input parameters and a short description of the output.
882!> \param bs_env ...
883! **************************************************************************************************
884 SUBROUTINE write_floquet_header(bs_env)
885 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
886
887 CHARACTER(LEN=*), PARAMETER :: routinen = 'write_floquet_header'
888
889 INTEGER :: handle, n_f_size, unit_nr
890
891 CALL timeset(routinen, handle)
892
893 unit_nr = bs_env%unit_nr
894 n_f_size = bs_env%n_ao*(1 + 2*bs_env%max_floquet_index)
895
896 IF (unit_nr > 0) THEN
897
898 WRITE (unit_nr, '(T2,A)') ' '
899 WRITE (unit_nr, '(T2,A)') repeat('-', 79)
900 WRITE (unit_nr, '(T2,A,A78)') '-', '-'
901 WRITE (unit_nr, '(T2,A,A51,A27)') '-', 'FLOQUET BANDSTRUCTURE CALCULATION', '-'
902 WRITE (unit_nr, '(T2,A,A78)') '-', '-'
903 WRITE (unit_nr, '(T2,A)') repeat('-', 79)
904 WRITE (unit_nr, '(T2,A)') ' '
905
906 WRITE (unit_nr, '(T2,A,T37,A,T67,ES12.2E2)') "FLOQUET PARAMETERS", "Amplitude [V/m]:", &
907 bs_env%floquet_amplitude*evolt/a_bohr
908 WRITE (unit_nr, '(T37,A,T67,F12.4)') "Frequency [eV]:", bs_env%floquet_omega*evolt
909 WRITE (unit_nr, '(T37,A)') repeat("-", 44)
910 WRITE (unit_nr, '(T37,A,T55,3F8.4)') "Polarisation:", bs_env%floquet_polarisation(1:3)
911 WRITE (unit_nr, '(T37,A,T55,3F8.4)') "Phase offsets:", pi*bs_env%floquet_phi(1:3)
912 WRITE (unit_nr, '(T37,A)') repeat("-", 44)
913 WRITE (unit_nr, '(T37,A,T67,I12)') "Max Floquet index:", bs_env%max_floquet_index
914 WRITE (unit_nr, '(T37,A,T67,I12)') "Floquet Hamiltonian Size:", n_f_size
915 WRITE (unit_nr, '(T37,A)') repeat("-", 44)
916 WRITE (unit_nr, '(T37,A,T67,F12.4)') &
917 "Energy window [eV]:", bs_env%energy_window_floquet*evolt
918 WRITE (unit_nr, '(T37,A,T67,F12.4)') "Energy step [eV]:", bs_env%energy_step_floquet*evolt
919 WRITE (unit_nr, '(T37,A,T67,F12.4)') "Broadening [eV]:", bs_env%broadening_floquet*evolt
920 WRITE (unit_nr, '(T37,A)') repeat("-", 44)
921 WRITE (unit_nr, '(A)') ""
922
923 WRITE (unit_nr, '(T2,A)') &
924 "We construct the Floquet-Bloch Hamiltonian and diagonalise it. Projecting the"
925 WRITE (unit_nr, '(T2,A)') &
926 Σα²"eigenvectors onto the Floquet sectors gives the weights w = _n |<n,m|>|,"
927 WRITE (unit_nr, '(T2,A)') &
928 "from which all of the following are obtained."
929 WRITE (unit_nr, '(A)') ""
930 WRITE (unit_nr, '(T2,A)') &
931 "The m=0 bands are the eigenvectors with the largest central-sector weight; they"
932 WRITE (unit_nr, '(T2,A)') &
933 "reduce to the equilibrium bands at zero field and are stored, with their"
934 WRITE (unit_nr, '(T2,A)') &
935 " weights, in FLOQUET_BANDSTRUCTURE.bs"
936 WRITE (unit_nr, '(A)') ""
937 WRITE (unit_nr, '(T2,A)') &
938 "Folding those bands into the first Floquet Brillouin zone, relative to the VBM,"
939 WRITE (unit_nr, '(T2,A)') &
940 "gives the quasi-energies stored in QUASI_ENERGIES.bs"
941 WRITE (unit_nr, '(A)') ""
942 WRITE (unit_nr, '(T2,A)') &
943 "The k-resolved density of states is obtained by computing the trace of"
944 WRITE (unit_nr, '(T2,A)') &
945 "the retarded Green's function and stored in FLOQUET_DOS.out"
946 WRITE (unit_nr, '(T2,A)') &
947 ωπω"DOS(,k) = -1/*Im[Tr_KS(G^R(,k))]"
948 WRITE (unit_nr, '(A)') ""
949 END IF
950
951 CALL timestop(handle)
952
953 END SUBROUTINE write_floquet_header
954
955! **************************************************************************************************
956!> \brief Sum the accumulated results across MPI ranks and write the m=0 band structure,
957!> the quasi-energies and the Floquet DOS to their files.
958!> \param bs_env ...
959!> \param floquet_env ...
960! **************************************************************************************************
961 SUBROUTINE write_floquet_results(bs_env, floquet_env)
962 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
963 TYPE(floquet_env_type), INTENT(INOUT) :: floquet_env
964
965 CHARACTER(LEN=*), PARAMETER :: routinen = 'write_floquet_results'
966
967 CHARACTER(LEN=default_string_length) :: fname
968 INTEGER :: bunit, handle, i, i_e, ikp_for_file, &
969 ispin, n_e, n_spin, nao, nkp_only_bs, &
970 nkp_start, qunit, wunit
971 REAL(kind=dp) :: e_min, energy, energy_step, f_occ, kt, &
972 mu, x
973 REAL(kind=dp), DIMENSION(3) :: xkp
974
975 CALL timeset(routinen, handle)
976
977 ! Collect all results on the global communicator and write them
978 CALL bs_env%para_env%sum(floquet_env%all_quasi_energies)
979 CALL bs_env%para_env%sum(floquet_env%all_a_k)
980 CALL bs_env%para_env%sum(floquet_env%all_m0_energies)
981 CALL bs_env%para_env%sum(floquet_env%all_m0_weights)
982
983 IF (bs_env%para_env%is_source()) THEN
984
985 nao = floquet_env%nao
986 n_spin = floquet_env%n_spin
987 nkp_only_bs = floquet_env%nkp_only_bs
988 n_e = floquet_env%n_E
989 nkp_start = bs_env%nkp_only_DOS
990
991 ! All three share one energy origin, the VBM.
992 mu = floquet_reference_energy(bs_env)
993 energy_step = bs_env%energy_step_floquet
994 e_min = mu - bs_env%energy_window_floquet
995
996 ! k_B T in Hartree (only used when TEMPERATURE > 0); E[K] = E[Hartree]*kelvin.
997 kt = bs_env%floquet_temperature/kelvin
998
999 ! Each file is opened once (REPLACE) and written for every band-structure k-point and spin.
1000
1001 ! The m=0 band structure
1002 WRITE (fname, "(2A)") trim(bs_env%floquet_bs_file), ".bs"
1003 CALL open_file(trim(fname), unit_number=bunit, file_status="REPLACE", file_action="WRITE")
1004 WRITE (bunit, "(A)") "# Floquet m=0 (central-sector) band structure"
1005 WRITE (bunit, "(A)") "# (in units of eV, relative to the VBM, not folded)"
1006 WRITE (bunit, "(A)") "# w = sum_n |<n,m=0|alpha>|^2 in [0,1]: w~1 clean m=0 replica,"
1007 WRITE (bunit, "(A)") "# w~0.5 hybridised with a sideband (drive near resonance)"
1008
1009 ! Quasi-energies
1010 WRITE (fname, "(2A)") trim(bs_env%floquet_qe_file), ".bs"
1011 CALL open_file(trim(fname), unit_number=qunit, file_status="REPLACE", file_action="WRITE")
1012 WRITE (qunit, "(A)") "# Quasi-energies obtained by diagonalising the Floquet Hamiltonian"
1013 WRITE (qunit, "(A)") "# (in units of eV, the m=0 bands relative to the VBM, folded to"
1014 WRITE (qunit, "(A)") "# the first Floquet Brillouin zone -hbar*Omega/2 < e <= hbar*Omega/2)"
1015
1016 ! DOS
1017 WRITE (fname, "(2A)") trim(bs_env%floquet_dos_file), ".out"
1018 CALL open_file(trim(fname), unit_number=wunit, file_status="REPLACE", file_action="WRITE")
1019 WRITE (wunit, "(A)") ωπω"# Floquet Density of States: D(,k) = -1/*Im[Tr_KS(G^R(,k))]"
1020
1021 DO ikp_for_file = 1, nkp_only_bs
1022 xkp(1:3) = bs_env%kpoints_DOS%xkp(1:3, nkp_start + ikp_for_file)
1023 DO ispin = 1, n_spin
1024
1025 ! <floquet_bs_file>.bs the m=0 band structure (absolute energies) and its weights
1026 WRITE (bunit, "(A,I0,T10,A,I0,A,T24,3(1X,F14.8))") &
1027 "# Spin ", ispin, " Point ", ikp_for_file, ": ", xkp(1:3)
1028 WRITE (bunit, "(A)") "# Floquet band Energy [eV] m=0 weight"
1029 DO i = 1, nao
1030 WRITE (bunit, "(I8,F21.8,F17.5)") i, &
1031 floquet_env%all_m0_energies(i, ispin, ikp_for_file)*evolt, &
1032 floquet_env%all_m0_weights(i, ispin, ikp_for_file)
1033 END DO
1034
1035 ! <floquet_qe_file>.bs Quasi-energies (folded to -ħΩ/2 < ε ≤ ħΩ/2)
1036 WRITE (qunit, "(A,I0,T10,A,I0,A,T24,3(1X,F14.8))") &
1037 "# Spin ", ispin, " Point ", ikp_for_file, ": ", xkp(1:3)
1038 WRITE (qunit, "(A)") "# Floquet band Quasi-energy [eV]"
1039 DO i = 1, nao
1040 WRITE (qunit, "(I8,F21.8)") i, &
1041 floquet_env%all_quasi_energies(i, ispin, ikp_for_file)*evolt
1042 END DO
1043
1044 ! <floquet_dos_file>.out the k-resolved DOS A(k,ω), and (if TEMPERATURE > 0) the
1045 ! occupied spectral weight f(E)*A(k,ω), f the Fermi-Dirac occupation at the VBM.
1046 WRITE (wunit, "(A,I0,T10,A,I0,A,T24,3(1X,F14.8))") &
1047 "# Spin ", ispin, " Point ", ikp_for_file, ": ", xkp(1:3)
1048 IF (bs_env%floquet_temperature > 0.0_dp) THEN
1049 WRITE (wunit, "(A)") ω"#Energy-VBM (eV) A(,k) = DOS (1/eV) f*A = occupied DOS (1/eV)"
1050 DO i_e = 1, n_e
1051 energy = e_min + i_e*energy_step
1052 x = (energy - mu)/kt ! (E - VBM)/k_B T, dimensionless
1053 IF (x > 40.0_dp) THEN ! guard EXP overflow at low T
1054 f_occ = 0.0_dp
1055 ELSE IF (x < -40.0_dp) THEN
1056 f_occ = 1.0_dp
1057 ELSE
1058 f_occ = 1.0_dp/(exp(x) + 1.0_dp)
1059 END IF
1060 WRITE (wunit, "(2X,3G13.4)") (energy - mu)*evolt, &
1061 floquet_env%all_a_k(i_e, ispin, ikp_for_file)/evolt, &
1062 f_occ*floquet_env%all_a_k(i_e, ispin, ikp_for_file)/evolt
1063 END DO
1064 ELSE
1065 WRITE (wunit, "(A)") ω"#Energy-VBM (eV) A(,k) = DOS (1/eV)"
1066 DO i_e = 1, n_e
1067 energy = e_min + i_e*energy_step
1068 WRITE (wunit, "(2X,2G13.4)") (energy - mu)*evolt, &
1069 floquet_env%all_a_k(i_e, ispin, ikp_for_file)/evolt
1070 END DO
1071 END IF
1072
1073 END DO
1074 END DO
1075
1076 CALL close_file(bunit)
1077 CALL close_file(qunit)
1078 CALL close_file(wunit)
1079 END IF
1080
1081 CALL timestop(handle)
1082
1083 END SUBROUTINE write_floquet_results
1084
1085END MODULE floquet_utils
Handles all functions related to the CELL.
Definition cell_types.F:15
subroutine, public get_cell(cell, alpha, beta, gamma, deth, orthorhombic, abc, periodic, h, h_inv, symmetry_id, tag)
Get informations about a simulation cell.
Definition cell_types.F:233
methods related to the blacs parallel environment
subroutine, public cp_blacs_env_create(blacs_env, para_env, blacs_grid_layout, blacs_repeatable, row_major, grid_2d)
allocates and initializes a type that represent a blacs context
Represents a complex full matrix distributed on many processors.
subroutine, public cp_cfm_get_submatrix(fm, target_m, start_row, start_col, n_rows, n_cols, transpose)
Extract a sub-matrix from the full matrix: op(target_m)(1:n_rows,1:n_cols) = fm(start_row:start_row+n...
subroutine, public cp_cfm_set_submatrix(matrix, new_values, start_row, start_col, n_rows, n_cols, alpha, beta, transpose)
Set a sub-matrix of the full matrix: matrix(start_row:start_row+n_rows,start_col:start_col+n_cols) = ...
Utility routines to open and close files. Tracking of preconnections.
Definition cp_files.F:16
subroutine, public open_file(file_name, file_status, file_form, file_action, file_position, file_pad, unit_number, debug, skip_get_unit_number, file_access)
Opens the requested file using a free unit number.
Definition cp_files.F:311
subroutine, public close_file(unit_number, file_status, keep_preconnection)
Close an open file given by its logical unit number. Optionally, keep the file and unit preconnected.
Definition cp_files.F:122
Environment type holding the work and accumulation arrays of the Floquet-Bloch band-structure calcula...
Helper routines for the Floquet-Bloch band-structure calculation (floquet_main).
subroutine, public floquet_sector_weights(floquet_env, cfm_eigenvectors)
Central and boundary-sector weights of every Floquet eigenvector, stored into floquet_envw0 and floqu...
subroutine, public check_floquet_convergence(bs_env, floquet_env)
Checks that MAX_FLOQUET_INDEX is large enough and the Floquet Hamiltonian was truncated far enough aw...
subroutine, public make_floquet_subgroups(qs_env, bs_env, para_env_sub, blacs_env_sub, group_distribution, ngroups)
Split the para_env into subgroups so that the Floquet Hamiltonian is distributed across the ranks of ...
subroutine, public write_floquet_header(bs_env)
Print the Floquet header with the input parameters and a short description of the output.
subroutine, public write_floquet_results(bs_env, floquet_env)
Sum the accumulated results across MPI ranks and write the m=0 band structure, the quasi-energies and...
subroutine, public compute_e_k_de_dk_dipole(qs_env, bs_env, e_k, de_dk, dipole)
Precompute, on the global communicator, the band quantities needed to assemble the Floquet-Bloch Hami...
subroutine, public build_floquet_matrix(bs_env, floquet_env, floquet_matrix)
Builds the Floquet-Bloch Hamiltonian H_F(k) for a single k-point and spin channel from the band data ...
subroutine, public calculate_floquet_observables(bs_env, para_env_sub, ispin, ikp, floquet_env)
Calculate all Floquet observables for one k-point and spin: the DOS (a_k), the m=0 bands,...
subroutine, public distribute_floquet_kp_data(para_env, para_env_sub, ispin, ikp, e_k, de_dk, dipole, floquet_env)
Make this k-point/spin's band data available to every rank of the owning subgroup.
collects all constants needed in input so that they can be used without circular dependencies
integer, parameter, public small_cell_full_kp
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public int_8
Definition kinds.F:54
integer, parameter, public dp
Definition kinds.F:34
integer, parameter, public default_string_length
Definition kinds.F:57
Implements transformations from k-space to R-space for Fortran array matrices.
subroutine, public rs_to_kp(rs_real, ks_complex, index_to_cell, xkp, deriv_direction, hmat)
Integrate RS matrices (stored as Fortran array) into a kpoint matrix at given kp.
subroutine, public replicate_rs_matrices(rs_dbcsr_in, kpoint_in, rs_array_out, cell_to_index_out)
Convert dbcsr matrices representing operators in real-space image cells to arrays.
Routines needed for kpoint calculation.
subroutine, public kpoint_init_cell_index(kpoint, sab_nl, para_env, nimages)
Generates the mapping of cell indices and linear RS index CELL (0,0,0) is always mapped to index 1.
Types and basic routines needed for a kpoint calculation.
subroutine, public get_kpoint_info(kpoint, kp_scheme, nkp_grid, kp_shift, symmetry, verbose, full_grid, use_real_wfn, eps_geo, parallel_group_size, kp_range, nkp, xkp, wkp, para_env, blacs_env_all, para_env_kp, para_env_inter_kp, blacs_env, kp_env, kp_aux_env, mpools, iogrp, nkp_groups, kp_dist, cell_to_index, index_to_cell, sab_nl, sab_nl_nosym, inversion_symmetry_only, symmetry_backend, symmetry_reduction_method, gamma_centered)
Retrieve information from a kpoint environment.
subroutine, public kpoint_release(kpoint)
Release a kpoint environment, deallocate all data.
subroutine, public kpoint_create(kpoint)
Create a kpoint environment.
Machine interface based on Fortran 2003 and POSIX.
Definition machine.F:17
subroutine, public m_memory_details(memtotal, memfree, buffers, cached, slab, sreclaimable, memlikelyfree)
get more detailed memory info, all units are bytes. the only 'useful' option is MemLikelyFree which i...
Definition machine.F:510
Definition of mathematical constants and functions.
real(kind=dp), parameter, public pi
complex(kind=dp), parameter, public z_one
complex(kind=dp), parameter, public gaussi
complex(kind=dp), parameter, public z_zero
Collection of simple mathematical functions and subroutines.
Definition mathlib.F:15
subroutine, public geeig_right(a_in, b_in, eigenvalues, eigenvectors)
Solve the generalized eigenvalue equation for complex matrices A*v = B*v*λ
Definition mathlib.F:2174
Interface to the message passing library MPI.
type(mp_split_type), parameter, public mp_comm_split_type_shared
Definition of physical constants:
Definition physcon.F:68
real(kind=dp), parameter, public a_bohr
Definition physcon.F:136
real(kind=dp), parameter, public kelvin
Definition physcon.F:165
real(kind=dp), parameter, public evolt
Definition physcon.F:183
subroutine, public get_qs_env(qs_env, atomic_kind_set, qs_kind_set, cell, super_cell, cell_ref, use_ref_cell, kpoints, dft_control, mos, sab_orb, sab_all, qmmm, qmmm_periodic, mimic, sac_ae, sac_ppl, sac_lri, sap_ppnl, sab_vdw, sab_scp, sap_oce, sab_lrc, sab_se, sab_xtbe, sab_tbe, sab_core, sab_xb, sab_xtb_pp, sab_xtb_nonbond, sab_almo, sab_kp, sab_kp_nosym, sab_cneo, particle_set, energy, force, matrix_h, matrix_h_im, matrix_ks, matrix_ks_im, matrix_vxc, run_rtp, rtp, matrix_h_kp, matrix_h_im_kp, matrix_ks_kp, matrix_ks_im_kp, matrix_vxc_kp, kinetic_kp, matrix_s_kp, matrix_w_kp, matrix_s_ri_aux_kp, matrix_s, matrix_s_ri_aux, matrix_w, matrix_p_mp2, matrix_p_mp2_admm, matrix_vhxc, rho, rho_xc, pw_env, ewald_env, ewald_pw, active_space, mpools, input, para_env, blacs_env, scf_control, rel_control, kinetic, qs_charges, vppl, xcint_weights, rho_core, rho_nlcc, rho_nlcc_g, ks_env, ks_qmmm_env, wf_history, scf_env, local_particles, local_molecules, distribution_2d, dbcsr_dist, molecule_kind_set, molecule_set, subsys, cp_subsys, oce, local_rho_set, rho_atom_set, task_list, task_list_soft, rho0_atom_set, rho0_mpole, rhoz_set, rhoz_cneo_set, ecoul_1c, rho0_s_rs, rho0_s_gs, rhoz_cneo_s_rs, rhoz_cneo_s_gs, do_kpoints, has_unit_metric, requires_mo_derivs, mo_derivs, mo_loc_history, nkind, natom, nelectron_total, nelectron_spin, efield, neighbor_list_id, linres_control, xas_env, virial, cp_ddapc_env, cp_ddapc_ewald, outer_scf_history, outer_scf_ihistory, x_data, et_coupling, dftb_potential, results, se_taper, se_store_int_env, se_nddo_mpole, se_nonbond_env, admm_env, lri_env, lri_density, exstate_env, ec_env, harris_env, dispersion_env, gcp_env, vee, rho_external, external_vxc, mask, mp2_env, bs_env, kg_env, wanniercentres, atprop, ls_scf_env, do_transport, transport_env, v_hartree_rspace, s_mstruct_changed, rho_changed, potential_changed, forces_up_to_date, mscfg_env, almo_scf_env, gradient_history, variable_history, embed_pot, spin_embed_pot, polar_env, mos_last_converged, eeq, rhs, do_rixs, tb_tblite)
Get the QUICKSTEP environment.
Definition and initialisation of the mo data type.
Definition qs_mo_types.F:22
subroutine, public get_mo_set(mo_set, maxocc, homo, lfomo, nao, nelectron, n_el_f, nmo, eigenvalues, occupation_numbers, mo_coeff, mo_coeff_b, uniform_occupation, kts, mu, flexible_electron_count)
Get the components of a MO set data structure.
Calculates the moment integrals <a|r^m|b> and <a|r x d/dr|b>
Definition qs_moments.F:14
subroutine, public qs_moment_kpoints_deep(qs_env, xkp, dipole, rcc, berry_c, do_parallel)
Calculates the dipole moments and berry curvature for periodic systems for kpoints.
Define the neighbor list data types and the corresponding functionality.
All kind of helpful little routines.
Definition util.F:14
Type defining parameters related to the simulation cell.
Definition cell_types.F:60
represent a blacs multidimensional parallel environment (for the mpi corrispective see cp_paratypes/m...
Represent a complex full matrix.
Work and accumulation arrays for one Floquet-Bloch band-structure run, plus the derived sizes....
Contains information about kpoints.
stores all the informations relevant to an mpi environment