Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief Routines needed for kpoint calculation
10 : !> \par History
11 : !> 2014.07 created [JGH]
12 : !> 2014.11 unified k-point and gamma-point code [Ole Schuett]
13 : !> \author JGH
14 : ! **************************************************************************************************
15 : MODULE kpoint_methods
16 : USE atomic_kind_types, ONLY: get_atomic_kind
17 : USE cell_types, ONLY: cell_type,&
18 : pbc_stable,&
19 : real_to_scaled
20 : USE cp_blacs_env, ONLY: cp_blacs_env_create,&
21 : cp_blacs_env_type
22 : USE cp_cfm_basic_linalg, ONLY: cp_cfm_column_scale,&
23 : cp_cfm_scale_and_add,&
24 : cp_cfm_transpose
25 : USE cp_cfm_types, ONLY: cp_cfm_create,&
26 : cp_cfm_get_info,&
27 : cp_cfm_get_submatrix,&
28 : cp_cfm_release,&
29 : cp_cfm_to_cfm,&
30 : cp_cfm_to_fm,&
31 : cp_cfm_type,&
32 : cp_fm_to_cfm
33 : USE cp_control_types, ONLY: hairy_probes_type
34 : USE cp_dbcsr_api, ONLY: &
35 : dbcsr_copy, dbcsr_create, dbcsr_deallocate_matrix, dbcsr_distribute, &
36 : dbcsr_distribution_get, dbcsr_distribution_type, dbcsr_get_block_p, dbcsr_get_info, &
37 : dbcsr_get_readonly_block_p, dbcsr_get_stored_coordinates, dbcsr_iterator_blocks_left, &
38 : dbcsr_iterator_next_block, dbcsr_iterator_readonly_start, dbcsr_iterator_start, &
39 : dbcsr_iterator_stop, dbcsr_iterator_type, dbcsr_p_type, dbcsr_replicate_all, dbcsr_set, &
40 : dbcsr_type, dbcsr_type_antisymmetric, dbcsr_type_no_symmetry, dbcsr_type_symmetric
41 : USE cp_dbcsr_contrib, ONLY: dbcsr_dot
42 : USE cp_dbcsr_cp2k_link, ONLY: cp_dbcsr_alloc_block_from_nbl
43 : USE cp_dbcsr_operations, ONLY: copy_fm_to_dbcsr
44 : USE cp_fm_basic_linalg, ONLY: cp_fm_column_scale
45 : USE cp_fm_pool_types, ONLY: cp_fm_pool_p_type,&
46 : fm_pool_create_fm,&
47 : fm_pool_give_back_fm
48 : USE cp_fm_struct, ONLY: cp_fm_struct_equivalent,&
49 : cp_fm_struct_type
50 : USE cp_fm_types, ONLY: &
51 : copy_info_type, cp_fm_cleanup_copy_general, cp_fm_create, cp_fm_finish_copy_general, &
52 : cp_fm_get_diag, cp_fm_get_info, cp_fm_get_submatrix, cp_fm_p_type, cp_fm_release, &
53 : cp_fm_start_copy_general, cp_fm_to_fm, cp_fm_type
54 : USE cp_log_handling, ONLY: cp_logger_get_default_io_unit
55 : USE cryssym, ONLY: crys_sym_gen,&
56 : csym_type,&
57 : kpoint_gen,&
58 : kpoint_gen_general,&
59 : print_crys_symmetry,&
60 : print_kp_symmetry,&
61 : release_csym_type
62 : USE hairy_probes, ONLY: probe_occupancy_kp
63 : USE input_constants, ONLY: smear_fermi_dirac,&
64 : smear_gaussian,&
65 : smear_mp,&
66 : smear_mv
67 : USE input_cp2k_kpoints, ONLY: lattice_fft_auto,&
68 : lattice_fft_off,&
69 : use_spglib_kpoint_backend,&
70 : use_spglib_kpoint_symmetry
71 : USE kinds, ONLY: dp,&
72 : int_8
73 : USE kpoint_lattice_fft, ONLY: cell_to_k_grid_fft,&
74 : k_grid_to_cell_execute,&
75 : k_grid_to_cell_prepare,&
76 : k_grid_to_cell_release,&
77 : k_grid_to_cell_work_type,&
78 : lattice_fft_shape,&
79 : regular_kpoint_grid
80 : USE kpoint_types, ONLY: &
81 : find_kpoint_rotation_slot, get_kpoint_info, kind_rotmat_type, kpoint_env_create, &
82 : kpoint_env_p_type, kpoint_env_type, kpoint_sym_create, kpoint_sym_type, kpoint_type
83 : USE local_gemm_api, ONLY: LOCAL_GEMM_PU_GPU,&
84 : LOCAL_GEMM_PU_HOST,&
85 : local_gemm_ctxt_type
86 : USE mathconstants, ONLY: twopi,&
87 : z_one,&
88 : z_zero
89 : USE mathlib, ONLY: inv_3x3
90 : USE memory_utilities, ONLY: reallocate
91 : USE message_passing, ONLY: mp_cart_type,&
92 : mp_para_env_type
93 : USE parallel_gemm_api, ONLY: parallel_gemm
94 : USE particle_types, ONLY: particle_type
95 : USE qs_matrix_pools, ONLY: mpools_create,&
96 : mpools_get,&
97 : mpools_rebuild_fm_pools,&
98 : qs_matrix_pools_type
99 : USE qs_mo_types, ONLY: allocate_mo_set,&
100 : get_mo_set,&
101 : init_mo_set,&
102 : mo_set_type,&
103 : set_mo_set
104 : USE qs_neighbor_list_types, ONLY: get_iterator_info,&
105 : get_neighbor_list_set_p,&
106 : neighbor_list_iterate,&
107 : neighbor_list_iterator_create,&
108 : neighbor_list_iterator_p_type,&
109 : neighbor_list_iterator_release,&
110 : neighbor_list_set_p_type
111 : USE scf_control_types, ONLY: smear_type
112 : USE smearing_utils, ONLY: Smearkp,&
113 : Smearkp2
114 : USE util, ONLY: get_limit,&
115 : sort
116 :
117 : !$ USE OMP_LIB, ONLY: omp_get_max_threads, &
118 : !$ omp_get_thread_num
119 : #include "./base/base_uses.f90"
120 :
121 : IMPLICIT NONE
122 :
123 : PRIVATE
124 :
125 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'kpoint_methods'
126 :
127 : TYPE, PUBLIC :: kp_transform_plan_type
128 : INTEGER :: nentry = 0, ngroup = 0
129 : LOGICAL :: symmetric = .FALSE.
130 : INTEGER, ALLOCATABLE, DIMENSION(:) :: col, col_offset, group_start, image, row, row_offset
131 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: cell
132 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: symmetry_sign
133 : END TYPE kp_transform_plan_type
134 :
135 : ! Non-owning view of one symmetry contribution; valid while kpoint metadata is unchanged.
136 : TYPE :: kp_symmetry_op_type
137 : TYPE(kind_rotmat_type), DIMENSION(:), POINTER :: kind_rot => NULL()
138 : INTEGER, DIMENSION(:), POINTER :: atom_map => NULL(), atom_kind => NULL()
139 : INTEGER, DIMENSION(:, :), POINTER :: cell_shift => NULL()
140 : REAL(KIND=dp), DIMENSION(3) :: xkp = 0.0_dp
141 : LOGICAL :: time_reversal = .FALSE., identity = .FALSE., redistribute = .FALSE.
142 : END TYPE kp_symmetry_op_type
143 :
144 : ! Non-owning operation metadata and owned, group-local density contributions.
145 : TYPE :: kp_density_group_type
146 : TYPE(kp_symmetry_op_type) :: op
147 : LOGICAL :: reverse_phase = .FALSE.
148 : INTEGER, ALLOCATABLE :: inverse(:), ikp(:)
149 : REAL(KIND=dp), ALLOCATABLE :: xkp(:, :), weight(:)
150 : END TYPE kp_density_group_type
151 :
152 : PUBLIC :: kpoint_initialize, kpoint_env_initialize, kpoint_initialize_mos, kpoint_initialize_mo_set
153 : PUBLIC :: kpoint_init_cell_index, kpoint_set_mo_occupation
154 : PUBLIC :: kpoint_smearing_edge_status
155 : PUBLIC :: kpoint_density_matrices, kpoint_density_transform
156 : PUBLIC :: kpoint_ot_energy_weighted_density
157 : PUBLIC :: kp_transform_plan_create
158 : PUBLIC :: rskp_transform, lowdin_kp_trans, lowdin_kp_mo_coeff
159 : PUBLIC :: rskp_grid_type, rskp_transform_grid_prepare, rskp_transform_grid_extract, &
160 : rskp_transform_grid_release
161 :
162 : ! **************************************************************************************************
163 :
164 : TYPE rskp_grid_type
165 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: values
166 : INTEGER, ALLOCATABLE, DIMENSION(:) :: block_col, block_nelem, block_offset, block_row
167 : LOGICAL :: ready = .FALSE.
168 : END TYPE rskp_grid_type
169 :
170 : ! **************************************************************************************************
171 :
172 : CONTAINS
173 :
174 : ! **************************************************************************************************
175 : !> \brief Index full-grid links by irreducible k-point without rescanning the complete mesh.
176 : !> \param representative irreducible representative of each full-grid link
177 : !> \param nkp number of irreducible k-points
178 : !> \param start offsets into order for each irreducible k-point
179 : !> \param order full-grid link indices grouped by irreducible representative
180 : ! **************************************************************************************************
181 1724 : SUBROUTINE kp_link_index_create(representative, nkp, start, order)
182 :
183 : INTEGER, INTENT(IN) :: representative(:), nkp
184 : INTEGER, ALLOCATABLE, INTENT(OUT) :: start(:), order(:)
185 :
186 : INTEGER :: i, ik
187 1724 : INTEGER, ALLOCATABLE :: count(:), next(:)
188 :
189 13792 : ALLOCATE (COUNT(nkp), next(nkp), start(nkp + 1), order(SIZE(representative)))
190 1724 : COUNT(:) = 0
191 21460 : DO i = 1, SIZE(representative)
192 19736 : ik = representative(i)
193 19736 : CPASSERT(ik >= 1 .AND. ik <= nkp)
194 21460 : COUNT(ik) = COUNT(ik) + 1
195 : END DO
196 1724 : start(1) = 1
197 5844 : DO ik = 1, nkp
198 4120 : start(ik + 1) = start(ik) + COUNT(ik)
199 5844 : next(ik) = start(ik)
200 : END DO
201 21460 : DO i = 1, SIZE(representative)
202 19736 : ik = representative(i)
203 19736 : order(next(ik)) = i
204 21460 : next(ik) = next(ik) + 1
205 : END DO
206 :
207 1724 : END SUBROUTINE kp_link_index_create
208 :
209 : ! **************************************************************************************************
210 : !> \brief Precompute lattice representations of crystallographic rotations.
211 : !> \param crys_sym crystallographic symmetry data
212 : !> \param cell simulation cell
213 : !> \param srot scaled-coordinate rotations
214 : !> \param frot integer direct-lattice rotations
215 : !> \param krot integer reciprocal-lattice rotations
216 : ! **************************************************************************************************
217 1724 : SUBROUTINE kp_rotation_cache_create(crys_sym, cell, srot, frot, krot)
218 :
219 : TYPE(csym_type), INTENT(IN) :: crys_sym
220 : TYPE(cell_type), POINTER :: cell
221 : REAL(KIND=dp), ALLOCATABLE, INTENT(OUT) :: srot(:, :, :)
222 : INTEGER, ALLOCATABLE, INTENT(OUT) :: frot(:, :, :), krot(:, :, :)
223 :
224 : INTEGER :: ic
225 :
226 10344 : ALLOCATE (srot(3, 3, crys_sym%nrtot), frot(3, 3, crys_sym%nrtot), krot(3, 3, crys_sym%nrtot))
227 27402 : DO ic = 1, crys_sym%nrtot
228 6573568 : srot(:, :, ic) = MATMUL(cell%h_inv, MATMUL(crys_sym%rt(:, :, ic), cell%hmat))
229 333814 : frot(:, :, ic) = NINT(srot(:, :, ic))
230 643674 : krot(:, :, ic) = NINT(TRANSPOSE(inv_3x3(REAL(frot(:, :, ic), KIND=dp))))
231 : END DO
232 :
233 1724 : END SUBROUTINE kp_rotation_cache_create
234 :
235 : ! **************************************************************************************************
236 : !> \brief Import symmetry metadata for one irreducible k-point.
237 : !> \param kpoint k-point environment
238 : !> \param crys_sym crystallographic symmetry data
239 : !> \param ik irreducible k-point index
240 : !> \param natom number of atoms
241 : !> \param scoord scaled atomic coordinates
242 : !> \param agauge periodic-image gauge of each atom
243 : !> \param eps_kpoint reciprocal-space matching tolerance
244 : !> \param link_start offsets into link_order
245 : !> \param link_order full-grid links grouped by irreducible representative
246 : !> \param srot_cache scaled-coordinate rotations
247 : !> \param frot_cache integer direct-lattice rotations
248 : !> \param krot_cache integer reciprocal-lattice rotations
249 : ! **************************************************************************************************
250 12238 : SUBROUTINE kp_symmetry_import(kpoint, crys_sym, ik, natom, scoord, agauge, eps_kpoint, &
251 : link_start, link_order, srot_cache, frot_cache, krot_cache)
252 :
253 : TYPE(kpoint_type), POINTER :: kpoint
254 : TYPE(csym_type), INTENT(IN) :: crys_sym
255 : INTEGER, INTENT(IN) :: ik, natom
256 : REAL(KIND=dp), INTENT(IN) :: scoord(:, :)
257 : INTEGER, INTENT(IN) :: agauge(:, :)
258 : REAL(KIND=dp), INTENT(IN) :: eps_kpoint
259 : INTEGER, ALLOCATABLE, INTENT(IN) :: link_start(:), link_order(:)
260 : REAL(KIND=dp), ALLOCATABLE, INTENT(IN) :: srot_cache(:, :, :)
261 : INTEGER, ALLOCATABLE, INTENT(IN) :: frot_cache(:, :, :), krot_cache(:, :, :)
262 :
263 : INTEGER :: ic, ilink, ir, ira, is, isign, j, nr, ns
264 : INTEGER, DIMENSION(3, 3) :: frot, krot
265 : REAL(KIND=dp), DIMENSION(3) :: diff, kgvec, srot
266 : REAL(KIND=dp), DIMENSION(3, 3) :: srotmat
267 : TYPE(kpoint_sym_type), POINTER :: kpsym
268 :
269 12238 : NULLIFY (kpoint%kp_sym(ik)%kpoint_sym)
270 12238 : CALL kpoint_sym_create(kpoint%kp_sym(ik)%kpoint_sym)
271 12238 : kpsym => kpoint%kp_sym(ik)%kpoint_sym
272 : IF (crys_sym%nrtot <= 0 .OR. crys_sym%fullgrid .OR. &
273 12702 : crys_sym%istriz /= 1 .OR. crys_sym%inversion_only) RETURN
274 :
275 4120 : CPASSERT(ALLOCATED(link_start) .AND. ALLOCATED(link_order))
276 4120 : CPASSERT(ALLOCATED(srot_cache) .AND. ALLOCATED(frot_cache) .AND. ALLOCATED(krot_cache))
277 4120 : kpsym%nwght = NINT(crys_sym%wkpoint(ik))
278 4120 : ns = kpsym%nwght
279 4120 : IF (ns <= 1) RETURN
280 :
281 : ! Count mappings not already recorded by kpoint_gen.
282 22928 : DO ilink = link_start(ik), link_start(ik + 1) - 1
283 19272 : is = link_order(ilink)
284 577140 : DO ic = 1, crys_sym%nrtot
285 7204756 : krot(1:3, 1:3) = krot_cache(1:3, 1:3, ic)
286 1681908 : DO isign = 1, 2
287 1108424 : ir = MERGE(crys_sym%ibrot(ic), -crys_sym%ibrot(ic), isign == 1)
288 1108424 : IF (ir == crys_sym%kpop(is)) CYCLE
289 : kgvec(1:3) = crys_sym%kpmesh(1:3, is) - &
290 : MATMUL(REAL(MERGE(krot(1:3, 1:3), -krot(1:3, 1:3), isign == 1), KIND=dp), &
291 30496256 : kpoint%xkp(1:3, ik))
292 4356608 : diff(1:3) = kgvec(1:3) - ANINT(kgvec(1:3))
293 2291612 : IF (ALL(ABS(diff(1:3)) < eps_kpoint)) ns = ns + 1
294 : END DO
295 : END DO
296 : END DO
297 :
298 3656 : kpsym%apply_symmetry = .TRUE.
299 25592 : ALLOCATE (kpsym%rot(3, 3, ns), kpsym%xkp(3, ns), kpsym%rotp(ns))
300 32904 : ALLOCATE (kpsym%f0(natom, ns), kpsym%fcell(3, natom, ns), kpsym%fcell_gauge(3, natom, ns))
301 7312 : ALLOCATE (kpsym%phase_mode(ns))
302 106848 : kpsym%phase_mode(:) = 0
303 :
304 3656 : nr = 0
305 22928 : DO ilink = link_start(ik), link_start(ik + 1) - 1
306 19272 : is = link_order(ilink)
307 19272 : nr = nr + 1
308 19272 : ir = crys_sym%kpop(is)
309 19272 : ira = ABS(ir)
310 101940 : DO ic = 1, crys_sym%nrtot
311 101940 : IF (crys_sym%ibrot(ic) /= ira) CYCLE
312 19272 : kpsym%rotp(nr) = ir
313 250536 : kpsym%rot(1:3, 1:3, nr) = crys_sym%rt(1:3, 1:3, ic)
314 250536 : srotmat(1:3, 1:3) = srot_cache(1:3, 1:3, ic)
315 250536 : frot(1:3, 1:3) = frot_cache(1:3, 1:3, ic)
316 77088 : kpsym%xkp(1:3, nr) = crys_sym%kpmesh(1:3, is)
317 142028 : kpsym%f0(1:natom, nr) = crys_sym%f0(1:natom, ic)
318 142028 : DO j = 1, natom
319 1964096 : srot(1:3) = MATMUL(srotmat, scoord(1:3, j)) + crys_sym%vt(1:3, ic)
320 491024 : kpsym%fcell(1:3, j, nr) = NINT(srot(1:3) - scoord(1:3, kpsym%f0(j, nr)))
321 : kpsym%fcell_gauge(1:3, j, nr) = kpsym%fcell(1:3, j, nr) + &
322 : MATMUL(frot, agauge(1:3, j)) - &
323 2351636 : agauge(1:3, kpsym%f0(j, nr))
324 : END DO
325 82668 : EXIT
326 : END DO
327 22928 : CPASSERT(ic <= crys_sym%nrtot)
328 : END DO
329 :
330 : ! Complete each star with equivalent operations not selected by kpoint_gen.
331 22928 : DO ilink = link_start(ik), link_start(ik + 1) - 1
332 19272 : is = link_order(ilink)
333 577140 : DO ic = 1, crys_sym%nrtot
334 7204756 : srotmat(1:3, 1:3) = srot_cache(1:3, 1:3, ic)
335 7204756 : frot(1:3, 1:3) = frot_cache(1:3, 1:3, ic)
336 7204756 : krot(1:3, 1:3) = krot_cache(1:3, 1:3, ic)
337 1681908 : DO isign = 1, 2
338 1108424 : ir = MERGE(crys_sym%ibrot(ic), -crys_sym%ibrot(ic), isign == 1)
339 1108424 : IF (ir == crys_sym%kpop(is)) CYCLE
340 : kgvec(1:3) = crys_sym%kpmesh(1:3, is) - &
341 : MATMUL(REAL(MERGE(krot(1:3, 1:3), -krot(1:3, 1:3), isign == 1), KIND=dp), &
342 30496256 : kpoint%xkp(1:3, ik))
343 4356608 : diff(1:3) = kgvec(1:3) - ANINT(kgvec(1:3))
344 1737400 : IF (.NOT. ALL(ABS(diff(1:3)) < eps_kpoint)) CYCLE
345 83920 : nr = nr + 1
346 83920 : kpsym%rotp(nr) = ir
347 1090960 : kpsym%rot(1:3, 1:3, nr) = crys_sym%rt(1:3, 1:3, ic)
348 335680 : kpsym%xkp(1:3, nr) = crys_sym%kpmesh(1:3, is)
349 546776 : kpsym%f0(1:natom, nr) = crys_sym%f0(1:natom, ic)
350 1100988 : DO j = 1, natom
351 7405696 : srot(1:3) = MATMUL(srotmat, scoord(1:3, j)) + crys_sym%vt(1:3, ic)
352 1851424 : kpsym%fcell(1:3, j, nr) = NINT(srot(1:3) - scoord(1:3, kpsym%f0(j, nr)))
353 : kpsym%fcell_gauge(1:3, j, nr) = kpsym%fcell(1:3, j, nr) + &
354 : MATMUL(frot, agauge(1:3, j)) - &
355 8878184 : agauge(1:3, kpsym%f0(j, nr))
356 : END DO
357 : END DO
358 : END DO
359 : END DO
360 3656 : CPASSERT(nr == ns)
361 3656 : kpsym%nwred = nr
362 :
363 : END SUBROUTINE kp_symmetry_import
364 :
365 : ! **************************************************************************************************
366 : !> \brief Generate the kpoints and initialize the kpoint environment
367 : !> \param kpoint The kpoint environment
368 : !> \param particle_set Particle types and coordinates
369 : !> \param cell Computational cell information
370 : ! **************************************************************************************************
371 11981 : SUBROUTINE kpoint_initialize(kpoint, particle_set, cell)
372 :
373 : TYPE(kpoint_type), POINTER :: kpoint
374 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
375 : TYPE(cell_type), POINTER :: cell
376 :
377 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_initialize'
378 :
379 : INTEGER :: handle, i, ik, iounit, j, natom, nkind, &
380 : ns
381 23962 : INTEGER, ALLOCATABLE, DIMENSION(:) :: atype, kp_link_order, kp_link_start
382 11981 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: agauge
383 11981 : INTEGER, ALLOCATABLE, DIMENSION(:, :, :) :: frot_cache, krot_cache
384 : LOGICAL :: spez
385 : REAL(KIND=dp) :: eps_kpoint, wsum
386 11981 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: coord, scoord
387 11981 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: srot_cache
388 : REAL(KIND=dp), DIMENSION(3) :: r_pbc, scoord_pbc
389 11981 : REAL(KIND=dp), DIMENSION(:), POINTER :: wkp_full
390 11981 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: xkp_full
391 227639 : TYPE(csym_type) :: crys_sym
392 :
393 11981 : CALL timeset(routineN, handle)
394 :
395 11981 : CPASSERT(ASSOCIATED(kpoint))
396 :
397 12011 : SELECT CASE (kpoint%kp_scheme)
398 : CASE ("NONE")
399 : ! do nothing
400 : CASE ("GAMMA")
401 30 : kpoint%nkp = 1
402 30 : ALLOCATE (kpoint%xkp(3, 1), kpoint%wkp(1))
403 120 : kpoint%xkp(1:3, 1) = 0.0_dp
404 30 : kpoint%wkp(1) = 1.0_dp
405 60 : ALLOCATE (kpoint%kp_sym(1))
406 30 : NULLIFY (kpoint%kp_sym(1)%kpoint_sym)
407 30 : CALL kpoint_sym_create(kpoint%kp_sym(1)%kpoint_sym)
408 : CASE ("MONKHORST-PACK", "MACDONALD")
409 :
410 3418 : IF (.NOT. kpoint%symmetry) THEN
411 : ! we set up a random molecule to avoid any possible symmetry
412 266 : natom = 10
413 266 : ALLOCATE (coord(3, natom), scoord(3, natom), atype(natom))
414 2926 : DO i = 1, natom
415 2660 : atype(i) = i
416 2660 : coord(1, i) = SIN(i*0.12345_dp)
417 2660 : coord(2, i) = COS(i*0.23456_dp)
418 2660 : coord(3, i) = SIN(i*0.34567_dp)
419 2926 : CALL real_to_scaled(scoord(1:3, i), coord(1:3, i), cell)
420 : END DO
421 : ELSE
422 3152 : natom = SIZE(particle_set)
423 15760 : ALLOCATE (scoord(3, natom), atype(natom))
424 17160 : DO i = 1, natom
425 14008 : CALL get_atomic_kind(atomic_kind=particle_set(i)%atomic_kind, kind_number=atype(i))
426 17160 : CALL real_to_scaled(scoord(1:3, i), particle_set(i)%r(1:3), cell)
427 : END DO
428 : END IF
429 3418 : IF (kpoint%verbose) THEN
430 2348 : iounit = cp_logger_get_default_io_unit()
431 : ELSE
432 1070 : iounit = -1
433 : END IF
434 : ! kind type list
435 10254 : ALLOCATE (kpoint%atype(natom))
436 20086 : kpoint%atype = atype
437 : ! Match the atom images used by CP2K's periodic neighbor lists.
438 10254 : ALLOCATE (agauge(3, natom))
439 3418 : agauge = 0
440 3418 : IF (kpoint%symmetry) THEN
441 17160 : DO i = 1, natom
442 14008 : r_pbc(1:3) = pbc_stable(particle_set(i)%r(1:3), cell)
443 14008 : CALL real_to_scaled(scoord_pbc, r_pbc, cell)
444 59184 : agauge(1:3, i) = NINT(scoord_pbc(1:3) - scoord(1:3, i))
445 : END DO
446 : END IF
447 :
448 : CALL crys_sym_gen(crys_sym, scoord, atype, cell%hmat, delta=kpoint%eps_geo, iounit=iounit, &
449 3418 : use_spglib=kpoint%symmetry)
450 : CALL kpoint_gen(crys_sym, kpoint%nkp_grid, symm=kpoint%symmetry, shift=kpoint%kp_shift, &
451 : full_grid=kpoint%full_grid, gamma_centered=kpoint%gamma_centered, &
452 : inversion_symmetry_only=kpoint%inversion_symmetry_only, &
453 : use_spglib_reduction= &
454 : kpoint%symmetry_reduction_method == use_spglib_kpoint_symmetry, &
455 3418 : use_spglib_backend=kpoint%symmetry_backend == use_spglib_kpoint_backend)
456 3418 : IF (crys_sym%inversion_only) kpoint%inversion_symmetry_only = .TRUE.
457 3418 : kpoint%nkp = crys_sym%nkpoint
458 17090 : ALLOCATE (kpoint%xkp(3, kpoint%nkp), kpoint%wkp(kpoint%nkp))
459 15630 : wsum = SUM(crys_sym%wkpoint)
460 15630 : DO ik = 1, kpoint%nkp
461 48848 : kpoint%xkp(1:3, ik) = crys_sym%xkpoint(1:3, ik)
462 15630 : kpoint%wkp(ik) = crys_sym%wkpoint(ik)/wsum
463 : END DO
464 : IF (crys_sym%nrtot > 0 .AND. .NOT. crys_sym%fullgrid .AND. &
465 3418 : crys_sym%istriz == 1 .AND. .NOT. crys_sym%inversion_only) THEN
466 1698 : CALL kp_link_index_create(crys_sym%kplink(2, :), kpoint%nkp, kp_link_start, kp_link_order)
467 1698 : CALL kp_rotation_cache_create(crys_sym, cell, srot_cache, frot_cache, krot_cache)
468 : END IF
469 :
470 3418 : eps_kpoint = MAX(1.e-12_dp, 10.0_dp*kpoint%eps_geo)
471 : ! print output
472 3418 : IF (kpoint%symmetry) CALL print_crys_symmetry(crys_sym)
473 3418 : IF (kpoint%symmetry) CALL print_kp_symmetry(crys_sym)
474 :
475 : ! transfer symmetry information
476 22466 : ALLOCATE (kpoint%kp_sym(kpoint%nkp))
477 15630 : DO ik = 1, kpoint%nkp
478 : CALL kp_symmetry_import(kpoint, crys_sym, ik, natom, scoord, agauge, eps_kpoint, &
479 15630 : kp_link_start, kp_link_order, srot_cache, frot_cache, krot_cache)
480 : END DO
481 3418 : IF (kpoint%symmetry) THEN
482 17160 : nkind = MAXVAL(atype)
483 3152 : ns = crys_sym%nrtot
484 42114 : ALLOCATE (kpoint%kind_rotmat(ns, nkind))
485 27582 : DO i = 1, ns
486 54136 : DO j = 1, nkind
487 50984 : NULLIFY (kpoint%kind_rotmat(i, j)%rmat)
488 : END DO
489 : END DO
490 8002 : ALLOCATE (kpoint%ibrot(ns))
491 27582 : kpoint%ibrot(1:ns) = crys_sym%ibrot(1:ns)
492 : END IF
493 :
494 3418 : CALL release_csym_type(crys_sym)
495 3418 : DEALLOCATE (scoord, atype)
496 3418 : DEALLOCATE (agauge)
497 :
498 : CASE ("GENERAL")
499 : NULLIFY (xkp_full, wkp_full)
500 36 : IF (ASSOCIATED(kpoint%xkp_input)) THEN
501 36 : xkp_full => kpoint%xkp_input
502 36 : wkp_full => kpoint%wkp_input
503 : ELSE
504 0 : xkp_full => kpoint%xkp
505 0 : wkp_full => kpoint%wkp
506 : END IF
507 36 : CPASSERT(ASSOCIATED(xkp_full))
508 36 : CPASSERT(ASSOCIATED(wkp_full))
509 36 : IF (.NOT. ASSOCIATED(kpoint%xkp_input)) THEN
510 0 : ALLOCATE (kpoint%xkp_input(3, SIZE(wkp_full)), kpoint%wkp_input(SIZE(wkp_full)))
511 0 : kpoint%xkp_input(1:3, 1:SIZE(wkp_full)) = xkp_full(1:3, 1:SIZE(wkp_full))
512 0 : kpoint%wkp_input(1:SIZE(wkp_full)) = wkp_full(1:SIZE(wkp_full))
513 0 : xkp_full => kpoint%xkp_input
514 0 : wkp_full => kpoint%wkp_input
515 : END IF
516 36 : IF (.NOT. kpoint%symmetry) THEN
517 10 : IF (.NOT. ASSOCIATED(kpoint%xkp)) THEN
518 0 : kpoint%nkp = SIZE(wkp_full)
519 0 : ALLOCATE (kpoint%xkp(3, kpoint%nkp), kpoint%wkp(kpoint%nkp))
520 0 : kpoint%xkp(1:3, 1:kpoint%nkp) = xkp_full(1:3, 1:kpoint%nkp)
521 0 : kpoint%wkp(1:kpoint%nkp) = wkp_full(1:kpoint%nkp)
522 : END IF
523 : ! default: no symmetry settings
524 74 : ALLOCATE (kpoint%kp_sym(kpoint%nkp))
525 54 : DO i = 1, kpoint%nkp
526 44 : NULLIFY (kpoint%kp_sym(i)%kpoint_sym)
527 54 : CALL kpoint_sym_create(kpoint%kp_sym(i)%kpoint_sym)
528 : END DO
529 : ELSE
530 26 : IF (kpoint%verbose) THEN
531 16 : iounit = cp_logger_get_default_io_unit()
532 : ELSE
533 10 : iounit = -1
534 : END IF
535 26 : natom = SIZE(particle_set)
536 130 : ALLOCATE (scoord(3, natom), atype(natom))
537 234 : DO i = 1, natom
538 208 : CALL get_atomic_kind(atomic_kind=particle_set(i)%atomic_kind, kind_number=atype(i))
539 234 : CALL real_to_scaled(scoord(1:3, i), particle_set(i)%r(1:3), cell)
540 : END DO
541 52 : ALLOCATE (kpoint%atype(natom))
542 234 : kpoint%atype = atype
543 78 : ALLOCATE (agauge(3, natom))
544 234 : DO i = 1, natom
545 208 : r_pbc(1:3) = pbc_stable(particle_set(i)%r(1:3), cell)
546 208 : CALL real_to_scaled(scoord_pbc, r_pbc, cell)
547 858 : agauge(1:3, i) = NINT(scoord_pbc(1:3) - scoord(1:3, i))
548 : END DO
549 :
550 : CALL crys_sym_gen(crys_sym, scoord, atype, cell%hmat, delta=kpoint%eps_geo, iounit=iounit, &
551 : use_spglib=(kpoint%symmetry_backend == use_spglib_kpoint_backend .OR. &
552 30 : kpoint%symmetry_reduction_method == use_spglib_kpoint_symmetry))
553 : CALL kpoint_gen_general(crys_sym, xkp_full, wkp_full, symm=kpoint%symmetry, &
554 : full_grid=kpoint%full_grid, &
555 : inversion_symmetry_only=kpoint%inversion_symmetry_only, &
556 : use_spglib_reduction= &
557 : kpoint%symmetry_reduction_method == use_spglib_kpoint_symmetry, &
558 26 : use_spglib_backend=kpoint%symmetry_backend == use_spglib_kpoint_backend)
559 26 : IF (crys_sym%inversion_only) kpoint%inversion_symmetry_only = .TRUE.
560 26 : IF (ASSOCIATED(kpoint%xkp)) THEN
561 26 : DEALLOCATE (kpoint%xkp)
562 26 : NULLIFY (kpoint%xkp)
563 : END IF
564 26 : IF (ASSOCIATED(kpoint%wkp)) THEN
565 26 : DEALLOCATE (kpoint%wkp)
566 26 : NULLIFY (kpoint%wkp)
567 : END IF
568 26 : kpoint%nkp = crys_sym%nkpoint
569 130 : ALLOCATE (kpoint%xkp(3, kpoint%nkp), kpoint%wkp(kpoint%nkp))
570 52 : wsum = SUM(crys_sym%wkpoint)
571 52 : DO ik = 1, kpoint%nkp
572 104 : kpoint%xkp(1:3, ik) = crys_sym%xkpoint(1:3, ik)
573 52 : kpoint%wkp(ik) = crys_sym%wkpoint(ik)/wsum
574 : END DO
575 : IF (crys_sym%nrtot > 0 .AND. .NOT. crys_sym%fullgrid .AND. &
576 26 : crys_sym%istriz == 1 .AND. .NOT. crys_sym%inversion_only) THEN
577 26 : CALL kp_link_index_create(crys_sym%kplink(2, :), kpoint%nkp, kp_link_start, kp_link_order)
578 26 : CALL kp_rotation_cache_create(crys_sym, cell, srot_cache, frot_cache, krot_cache)
579 : END IF
580 :
581 26 : eps_kpoint = MAX(1.e-12_dp, 10.0_dp*kpoint%eps_geo)
582 26 : CALL print_crys_symmetry(crys_sym)
583 26 : CALL print_kp_symmetry(crys_sym)
584 :
585 104 : ALLOCATE (kpoint%kp_sym(kpoint%nkp))
586 52 : DO ik = 1, kpoint%nkp
587 : CALL kp_symmetry_import(kpoint, crys_sym, ik, natom, scoord, agauge, eps_kpoint, &
588 52 : kp_link_start, kp_link_order, srot_cache, frot_cache, krot_cache)
589 : END DO
590 234 : nkind = MAXVAL(atype)
591 26 : ns = crys_sym%nrtot
592 1378 : ALLOCATE (kpoint%kind_rotmat(ns, nkind))
593 1274 : DO i = 1, ns
594 2522 : DO j = 1, nkind
595 2496 : NULLIFY (kpoint%kind_rotmat(i, j)%rmat)
596 : END DO
597 : END DO
598 78 : ALLOCATE (kpoint%ibrot(ns))
599 1274 : kpoint%ibrot(1:ns) = crys_sym%ibrot(1:ns)
600 :
601 26 : CALL release_csym_type(crys_sym)
602 26 : DEALLOCATE (scoord, atype)
603 26 : DEALLOCATE (agauge)
604 : END IF
605 : CASE DEFAULT
606 11981 : CPABORT("Option invalid or unavailable for kpoint%kp_scheme")
607 : END SELECT
608 :
609 : ! check for consistency of options
610 12011 : SELECT CASE (kpoint%kp_scheme)
611 : CASE ("NONE")
612 : ! don't use k-point code
613 : CASE ("GAMMA")
614 30 : CPASSERT(kpoint%nkp == 1)
615 150 : CPASSERT(SUM(ABS(kpoint%xkp)) <= 1.e-12_dp)
616 30 : CPASSERT(kpoint%wkp(1) == 1.0_dp)
617 30 : CPASSERT(.NOT. kpoint%symmetry)
618 : CASE ("GENERAL")
619 36 : CPASSERT(kpoint%nkp >= 1)
620 : CASE ("MONKHORST-PACK", "MACDONALD")
621 11981 : CPASSERT(kpoint%nkp >= 1)
622 : END SELECT
623 11981 : IF (kpoint%use_real_wfn) THEN
624 : ! what about inversion symmetry?
625 60 : ikloop: DO ik = 1, kpoint%nkp
626 150 : DO i = 1, 3
627 90 : spez = (kpoint%xkp(i, ik) == 0.0_dp .OR. kpoint%xkp(i, ik) == 0.5_dp)
628 30 : IF (.NOT. spez) EXIT ikloop
629 : END DO
630 : END DO ikloop
631 30 : IF (.NOT. spez) THEN
632 : ! Warning: real wfn might be wrong for this system
633 : CALL cp_warn(__LOCATION__, &
634 : "A calculation using real wavefunctions is requested. "// &
635 0 : "We could not determine if the symmetry of the system allows real wavefunctions. ")
636 : END IF
637 : END IF
638 :
639 11981 : CALL timestop(handle)
640 :
641 23962 : END SUBROUTINE kpoint_initialize
642 :
643 : ! **************************************************************************************************
644 : !> \brief Initialize the kpoint environment
645 : !> \param kpoint Kpoint environment
646 : !> \param para_env ...
647 : !> \param blacs_env ...
648 : !> \param with_aux_fit ...
649 : ! **************************************************************************************************
650 3252 : SUBROUTINE kpoint_env_initialize(kpoint, para_env, blacs_env, with_aux_fit)
651 :
652 : TYPE(kpoint_type), INTENT(INOUT) :: kpoint
653 : TYPE(mp_para_env_type), INTENT(IN), TARGET :: para_env
654 : TYPE(cp_blacs_env_type), INTENT(IN), TARGET :: blacs_env
655 : LOGICAL, INTENT(IN), OPTIONAL :: with_aux_fit
656 :
657 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_env_initialize'
658 :
659 : INTEGER :: handle, igr, ngr, niogrp, nkp, &
660 : nkp_grp, nkp_loc, npe, unit_nr
661 : INTEGER, DIMENSION(2) :: dims, pos
662 : LOGICAL :: aux_fit
663 3252 : TYPE(mp_cart_type) :: comm_cart
664 : TYPE(mp_para_env_type), POINTER :: para_env_inter_kp, para_env_kp
665 :
666 3252 : CALL timeset(routineN, handle)
667 :
668 3252 : IF (PRESENT(with_aux_fit)) THEN
669 3104 : aux_fit = with_aux_fit
670 : ELSE
671 : aux_fit = .FALSE.
672 : END IF
673 :
674 3252 : kpoint%para_env => para_env
675 3252 : CALL kpoint%para_env%retain()
676 3252 : kpoint%blacs_env_all => blacs_env
677 3252 : CALL kpoint%blacs_env_all%retain()
678 :
679 3252 : CPASSERT(.NOT. ASSOCIATED(kpoint%kp_env))
680 3252 : IF (aux_fit) THEN
681 32 : CPASSERT(.NOT. ASSOCIATED(kpoint%kp_aux_env))
682 : END IF
683 :
684 3252 : nkp = kpoint%nkp
685 3252 : npe = para_env%num_pe
686 3252 : IF (npe == 1) THEN
687 : ! only one process available -> owns all kpoints
688 0 : ALLOCATE (kpoint%kp_dist(2, 1))
689 0 : kpoint%kp_dist(1, 1) = 1
690 0 : kpoint%kp_dist(2, 1) = nkp
691 0 : kpoint%kp_range(1) = 1
692 0 : kpoint%kp_range(2) = nkp
693 :
694 : ! parallel environments
695 0 : kpoint%para_env_kp => para_env
696 0 : CALL kpoint%para_env_kp%retain()
697 0 : kpoint%para_env_inter_kp => para_env
698 0 : CALL kpoint%para_env_inter_kp%retain()
699 0 : kpoint%iogrp = .TRUE.
700 0 : kpoint%nkp_groups = 1
701 : ELSE
702 3252 : IF (kpoint%parallel_group_size == -1) THEN
703 : ! Maximum parallelization over kpoints with equal-sized MPI groups.
704 : ! Each group must own at least one kpoint; their kpoint counts may differ.
705 8388 : DO igr = npe, 1, -1
706 5592 : IF (MOD(npe, igr) /= 0) CYCLE
707 5592 : nkp_grp = npe/igr
708 5592 : IF (nkp_grp > nkp) CYCLE
709 8388 : ngr = igr
710 : END DO
711 456 : ELSE IF (kpoint%parallel_group_size == 0) THEN
712 : ! no parallelization over kpoints
713 338 : ngr = npe
714 118 : ELSE IF (kpoint%parallel_group_size > 0) THEN
715 118 : ngr = MIN(kpoint%parallel_group_size, npe)
716 : ELSE
717 0 : CPABORT("kpoint%parallel_group_size cannot be smaller than -1")
718 : END IF
719 3252 : nkp_grp = npe/ngr
720 : ! processor dimensions
721 3252 : dims(1) = ngr
722 3252 : dims(2) = nkp_grp
723 3252 : IF ((dims(1)*dims(2) /= npe)) THEN
724 0 : CPABORT("Number of processors is not divisible by the kpoint group size.")
725 : END IF
726 3252 : IF (nkp_grp > nkp) THEN
727 0 : CPABORT("Too many kpoint groups. Increase PARALLEL_GROUP_SIZE.")
728 : END IF
729 :
730 : ! Create the subgroups, one for each k-point group and one interconnecting group
731 3252 : CALL comm_cart%create(comm_old=para_env, ndims=2, dims=dims)
732 9756 : pos = comm_cart%mepos_cart
733 3252 : ALLOCATE (para_env_kp)
734 3252 : CALL para_env_kp%from_split(comm_cart, pos(2))
735 3252 : ALLOCATE (para_env_inter_kp)
736 3252 : CALL para_env_inter_kp%from_split(comm_cart, pos(1))
737 3252 : CALL comm_cart%free()
738 :
739 3252 : niogrp = 0
740 3252 : IF (para_env%is_source()) niogrp = 1
741 3252 : CALL para_env_kp%sum(niogrp)
742 3252 : kpoint%iogrp = (niogrp == 1)
743 :
744 : ! parallel groups
745 3252 : kpoint%para_env_kp => para_env_kp
746 3252 : kpoint%para_env_inter_kp => para_env_inter_kp
747 :
748 : ! distribution of kpoints
749 9756 : ALLOCATE (kpoint%kp_dist(2, nkp_grp))
750 8600 : DO igr = 1, nkp_grp
751 19296 : kpoint%kp_dist(1:2, igr) = get_limit(nkp, nkp_grp, igr - 1)
752 : END DO
753 : ! local kpoints
754 9756 : kpoint%kp_range(1:2) = kpoint%kp_dist(1:2, para_env_inter_kp%mepos + 1)
755 3252 : nkp_loc = kpoint%kp_range(2) - kpoint%kp_range(1) + 1
756 :
757 3252 : unit_nr = cp_logger_get_default_io_unit()
758 :
759 3252 : IF (unit_nr > 0 .AND. kpoint%verbose) THEN
760 1183 : WRITE (unit_nr, *)
761 1183 : WRITE (unit_nr, FMT="(T2,A,T71,I10)") "KPOINTS| Total number of kpoints", nkp
762 1183 : WRITE (unit_nr, FMT="(T2,A,T71,I10)") "KPOINTS| Number of kpoint groups ", nkp_grp
763 1183 : WRITE (unit_nr, FMT="(T2,A,T71,I10)") "KPOINTS| Size of each kpoint group", ngr
764 1183 : IF (MOD(nkp, nkp_grp) == 0) THEN
765 1166 : WRITE (unit_nr, FMT="(T2,A,T71,I10)") "KPOINTS| Number of kpoints per group", nkp_loc
766 : ELSE
767 17 : WRITE (unit_nr, FMT="(T2,A,T71,I10)") "KPOINTS| Minimum number of kpoints per group", nkp/nkp_grp
768 17 : WRITE (unit_nr, FMT="(T2,A,T71,I10)") "KPOINTS| Maximum number of kpoints per group", nkp/nkp_grp + 1
769 : END IF
770 : END IF
771 3252 : kpoint%nkp_groups = nkp_grp
772 :
773 : END IF
774 :
775 3252 : nkp_loc = kpoint%kp_range(2) - kpoint%kp_range(1) + 1
776 3252 : CALL create_local_environments(kpoint%kp_env)
777 3252 : IF (aux_fit) CALL create_local_environments(kpoint%kp_aux_env)
778 :
779 3252 : CALL timestop(handle)
780 :
781 : CONTAINS
782 :
783 : ! **************************************************************************************************
784 : !> \brief ...
785 : !> \param env ...
786 : ! **************************************************************************************************
787 3284 : SUBROUTINE create_local_environments(env)
788 : TYPE(kpoint_env_p_type), INTENT(OUT), POINTER :: env(:)
789 :
790 : INTEGER :: ik, ikk
791 : TYPE(kpoint_env_type), POINTER :: kp
792 :
793 17585 : ALLOCATE (env(nkp_loc))
794 11017 : DO ik = 1, nkp_loc
795 7733 : ikk = kpoint%kp_range(1) + ik - 1
796 7733 : CALL kpoint_env_create(env(ik)%kpoint_env)
797 7733 : kp => env(ik)%kpoint_env
798 7733 : kp%nkpoint = ikk
799 7733 : kp%wkp = kpoint%wkp(ikk)
800 30932 : kp%xkp(1:3) = kpoint%xkp(1:3, ikk)
801 11017 : kp%is_local = (kpoint%para_env_kp%num_pe == 1)
802 : END DO
803 3284 : END SUBROUTINE create_local_environments
804 :
805 : END SUBROUTINE kpoint_env_initialize
806 :
807 : ! **************************************************************************************************
808 : !> \brief Initialize a set of MOs and density matrix for each kpoint (kpoint group)
809 : !> \param kpoint Kpoint environment
810 : !> \param mos Reference MOs (global)
811 : !> \param added_mos ...
812 : !> \param for_aux_fit ...
813 : ! **************************************************************************************************
814 3294 : SUBROUTINE kpoint_initialize_mos(kpoint, mos, added_mos, for_aux_fit)
815 :
816 : TYPE(kpoint_type), POINTER :: kpoint
817 : TYPE(mo_set_type), DIMENSION(:), INTENT(INOUT) :: mos
818 : INTEGER, INTENT(IN), OPTIONAL :: added_mos
819 : LOGICAL, OPTIONAL :: for_aux_fit
820 :
821 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_initialize_mos'
822 :
823 : INTEGER :: handle, ic, ik, is, nadd, nao, nc, &
824 : nelectron, nkp_loc, nmo, nmorig(2), &
825 : nspin
826 : LOGICAL :: aux_fit
827 : REAL(KIND=dp) :: flexible_electron_count, maxocc, n_el_f
828 : TYPE(cp_blacs_env_type), POINTER :: blacs_env
829 3294 : TYPE(cp_fm_pool_p_type), DIMENSION(:), POINTER :: ao_ao_fm_pools
830 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct
831 : TYPE(cp_fm_type), POINTER :: fmlocal
832 : TYPE(kpoint_env_type), POINTER :: kp
833 : TYPE(qs_matrix_pools_type), POINTER :: mpools
834 :
835 3294 : CALL timeset(routineN, handle)
836 :
837 3294 : IF (PRESENT(for_aux_fit)) THEN
838 32 : aux_fit = for_aux_fit
839 : ELSE
840 : aux_fit = .FALSE.
841 : END IF
842 :
843 3294 : CPASSERT(ASSOCIATED(kpoint))
844 :
845 3294 : IF (aux_fit) THEN
846 32 : CPASSERT(ASSOCIATED(kpoint%kp_aux_env))
847 : END IF
848 :
849 3294 : IF (PRESENT(added_mos)) THEN
850 130 : nadd = added_mos
851 : ELSE
852 : nadd = 0
853 : END IF
854 :
855 3294 : IF (kpoint%use_real_wfn) THEN
856 : nc = 1
857 : ELSE
858 3266 : nc = 2
859 : END IF
860 3294 : nspin = SIZE(mos, 1)
861 3294 : nkp_loc = kpoint%kp_range(2) - kpoint%kp_range(1) + 1
862 3294 : IF (nkp_loc > 0) THEN
863 3294 : IF (aux_fit) THEN
864 32 : CPASSERT(SIZE(kpoint%kp_aux_env) == nkp_loc)
865 : ELSE
866 3262 : CPASSERT(SIZE(kpoint%kp_env) == nkp_loc)
867 : END IF
868 : ! Allocate one MO set per spin; its coefficient matrix owns the scalar type.
869 11039 : DO ik = 1, nkp_loc
870 7745 : IF (aux_fit) THEN
871 171 : kp => kpoint%kp_aux_env(ik)%kpoint_env
872 : ELSE
873 7574 : kp => kpoint%kp_env(ik)%kpoint_env
874 : END IF
875 31714 : ALLOCATE (kp%mos(nspin))
876 16224 : DO is = 1, nspin
877 : CALL get_mo_set(mos(is), nao=nao, nmo=nmo, nelectron=nelectron, &
878 8479 : n_el_f=n_el_f, maxocc=maxocc, flexible_electron_count=flexible_electron_count)
879 8479 : nmo = MIN(nao, nmo + nadd)
880 : CALL allocate_mo_set(kp%mos(is), nao, nmo, nelectron, n_el_f, maxocc, &
881 16224 : flexible_electron_count)
882 : END DO
883 : ! freshly allocated MOS carry no coefficients: solvers must
884 : ! cold-start again until an extrapolation refills them
885 11039 : kp%mos_prefilled = .FALSE.
886 : END DO
887 :
888 : ! generate the blacs environment for the kpoint group
889 : ! we generate a blacs env for each kpoint group in parallel
890 : ! we assume here that the group para_env_inter_kp will connect
891 : ! equivalent parts of fm matrices, i.e. no reshuffeling of processors
892 3294 : NULLIFY (blacs_env)
893 3294 : IF (ASSOCIATED(kpoint%blacs_env)) THEN
894 42 : blacs_env => kpoint%blacs_env
895 : ELSE
896 3252 : CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=kpoint%para_env_kp)
897 3252 : kpoint%blacs_env => blacs_env
898 : END IF
899 :
900 : ! set possible new number of MOs
901 6814 : DO is = 1, nspin
902 3520 : CALL get_mo_set(mos(is), nmo=nmorig(is))
903 3520 : nmo = MIN(nao, nmorig(is) + nadd)
904 6814 : CALL set_mo_set(mos(is), nmo=nmo)
905 : END DO
906 : ! matrix pools for the kpoint group, information on MOs is transferred using
907 : ! generic mos structure
908 3294 : NULLIFY (mpools)
909 3294 : CALL mpools_create(mpools=mpools)
910 : CALL mpools_rebuild_fm_pools(mpools=mpools, mos=mos, &
911 3294 : blacs_env=blacs_env, para_env=kpoint%para_env_kp)
912 :
913 3294 : IF (aux_fit) THEN
914 32 : kpoint%mpools_aux_fit => mpools
915 : ELSE
916 3262 : kpoint%mpools => mpools
917 : END IF
918 :
919 : ! reset old number of MOs
920 6814 : DO is = 1, nspin
921 6814 : CALL set_mo_set(mos(is), nmo=nmorig(is))
922 : END DO
923 :
924 : ! allocate density matrices
925 3294 : CALL mpools_get(mpools, ao_ao_fm_pools=ao_ao_fm_pools)
926 3294 : ALLOCATE (fmlocal)
927 3294 : CALL fm_pool_create_fm(ao_ao_fm_pools(1)%pool, fmlocal)
928 3294 : CALL cp_fm_get_info(fmlocal, matrix_struct=matrix_struct)
929 11039 : DO ik = 1, nkp_loc
930 7745 : IF (aux_fit) THEN
931 171 : kp => kpoint%kp_aux_env(ik)%kpoint_env
932 : ELSE
933 7574 : kp => kpoint%kp_env(ik)%kpoint_env
934 : END IF
935 : ! density matrix
936 7745 : CALL cp_fm_release(kp%pmat)
937 56387 : ALLOCATE (kp%pmat(nc, nspin))
938 16224 : DO is = 1, nspin
939 33152 : DO ic = 1, nc
940 25407 : CALL cp_fm_create(kp%pmat(ic, is), matrix_struct)
941 : END DO
942 : END DO
943 : ! energy weighted density matrix
944 7745 : CALL cp_fm_release(kp%wmat)
945 48642 : ALLOCATE (kp%wmat(nc, nspin))
946 19518 : DO is = 1, nspin
947 33152 : DO ic = 1, nc
948 25407 : CALL cp_fm_create(kp%wmat(ic, is), matrix_struct)
949 : END DO
950 : END DO
951 : END DO
952 3294 : CALL fm_pool_give_back_fm(ao_ao_fm_pools(1)%pool, fmlocal)
953 3294 : DEALLOCATE (fmlocal)
954 :
955 : END IF
956 :
957 3294 : CALL timestop(handle)
958 :
959 3294 : END SUBROUTINE kpoint_initialize_mos
960 :
961 : ! **************************************************************************************************
962 : !> \brief ...
963 : !> \param kpoint ...
964 : ! **************************************************************************************************
965 158 : SUBROUTINE kpoint_initialize_mo_set(kpoint)
966 : TYPE(kpoint_type), POINTER :: kpoint
967 :
968 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_initialize_mo_set'
969 :
970 : INTEGER :: handle, ik, ispin
971 158 : TYPE(cp_fm_pool_p_type), DIMENSION(:), POINTER :: ao_mo_fm_pools
972 158 : TYPE(mo_set_type), DIMENSION(:), POINTER :: moskp
973 :
974 158 : CALL timeset(routineN, handle)
975 :
976 1474 : DO ik = 1, SIZE(kpoint%kp_env)
977 1316 : CALL mpools_get(kpoint%mpools, ao_mo_fm_pools=ao_mo_fm_pools)
978 1316 : moskp => kpoint%kp_env(ik)%kpoint_env%mos
979 1316 : CPASSERT(ASSOCIATED(moskp))
980 2868 : DO ispin = 1, SIZE(moskp)
981 1394 : IF (ASSOCIATED(moskp(ispin)%mo_coeff) .OR. &
982 : ASSOCIATED(moskp(ispin)%cmo_coeff)) CYCLE
983 : CALL init_mo_set(moskp(ispin), fm_pool=ao_mo_fm_pools(ispin)%pool, &
984 2710 : name="kpoints", complex_coeff=.NOT. kpoint%use_real_wfn)
985 : END DO
986 : END DO
987 :
988 158 : CALL timestop(handle)
989 :
990 158 : END SUBROUTINE kpoint_initialize_mo_set
991 :
992 : ! **************************************************************************************************
993 : !> \brief Generates the mapping of cell indices and linear RS index
994 : !> CELL (0,0,0) is always mapped to index 1
995 : !> \param kpoint Kpoint environment
996 : !> \param sab_nl Defining neighbour list
997 : !> \param para_env Parallel environment
998 : !> \param nimages [output]
999 : ! **************************************************************************************************
1000 4474 : SUBROUTINE kpoint_init_cell_index(kpoint, sab_nl, para_env, nimages)
1001 :
1002 : TYPE(kpoint_type), POINTER :: kpoint
1003 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1004 : POINTER :: sab_nl
1005 : TYPE(mp_para_env_type), POINTER :: para_env
1006 : INTEGER, INTENT(OUT) :: nimages
1007 :
1008 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_init_cell_index'
1009 :
1010 : INTEGER :: handle, i1, i2, i3, ic, icount, it, &
1011 : ncount
1012 : INTEGER, DIMENSION(3) :: cell, itm
1013 4474 : INTEGER, DIMENSION(:, :), POINTER :: index_to_cell, list
1014 4474 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index, cti
1015 : LOGICAL :: new
1016 : TYPE(neighbor_list_iterator_p_type), &
1017 4474 : DIMENSION(:), POINTER :: nl_iterator
1018 :
1019 4474 : NULLIFY (cell_to_index, index_to_cell)
1020 :
1021 4474 : CALL timeset(routineN, handle)
1022 :
1023 4474 : CPASSERT(ASSOCIATED(kpoint))
1024 :
1025 4474 : ALLOCATE (list(3, 125))
1026 2241474 : list = 0
1027 4474 : icount = 1
1028 :
1029 4474 : CALL neighbor_list_iterator_create(nl_iterator, sab_nl)
1030 1428743 : DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
1031 1424269 : CALL get_iterator_info(nl_iterator, cell=cell)
1032 :
1033 1424269 : new = .TRUE.
1034 91607500 : DO ic = 1, icount
1035 91358013 : IF (cell(1) == list(1, ic) .AND. cell(2) == list(2, ic) .AND. &
1036 249487 : cell(3) == list(3, ic)) THEN
1037 : new = .FALSE.
1038 : EXIT
1039 : END IF
1040 : END DO
1041 1428743 : IF (new) THEN
1042 249487 : icount = icount + 1
1043 249487 : IF (icount > SIZE(list, 2)) THEN
1044 681 : CALL reallocate(list, 1, 3, 1, 2*SIZE(list, 2))
1045 : END IF
1046 997948 : list(1:3, icount) = cell(1:3)
1047 : END IF
1048 :
1049 : END DO
1050 4474 : CALL neighbor_list_iterator_release(nl_iterator)
1051 :
1052 258435 : itm(1) = MAXVAL(ABS(list(1, 1:icount)))
1053 258435 : itm(2) = MAXVAL(ABS(list(2, 1:icount)))
1054 258435 : itm(3) = MAXVAL(ABS(list(3, 1:icount)))
1055 4474 : CALL para_env%max(itm)
1056 17896 : it = MAXVAL(itm(1:3))
1057 4474 : IF (ASSOCIATED(kpoint%cell_to_index)) THEN
1058 4470 : DEALLOCATE (kpoint%cell_to_index)
1059 : END IF
1060 22370 : ALLOCATE (kpoint%cell_to_index(-itm(1):itm(1), -itm(2):itm(2), -itm(3):itm(3)))
1061 4474 : cell_to_index => kpoint%cell_to_index
1062 4474 : cti => cell_to_index
1063 731680 : cti(:, :, :) = 0
1064 258435 : DO ic = 1, icount
1065 253961 : i1 = list(1, ic)
1066 253961 : i2 = list(2, ic)
1067 253961 : i3 = list(3, ic)
1068 258435 : cti(i1, i2, i3) = ic
1069 : END DO
1070 1458886 : CALL para_env%sum(cti)
1071 4474 : ncount = 0
1072 24188 : DO i1 = -itm(1), itm(1)
1073 133986 : DO i2 = -itm(2), itm(2)
1074 737342 : DO i3 = -itm(3), itm(3)
1075 717628 : IF (cti(i1, i2, i3) == 0) THEN
1076 295406 : cti(i1, i2, i3) = 1000000
1077 : ELSE
1078 312424 : ncount = ncount + 1
1079 312424 : cti(i1, i2, i3) = (ABS(i1) + ABS(i2) + ABS(i3))*1000 + ABS(i3)*100 + ABS(i2)*10 + ABS(i1)
1080 312424 : cti(i1, i2, i3) = cti(i1, i2, i3) + (i1 + i2 + i3)
1081 : END IF
1082 : END DO
1083 : END DO
1084 : END DO
1085 :
1086 4474 : IF (ASSOCIATED(kpoint%index_to_cell)) THEN
1087 4474 : DEALLOCATE (kpoint%index_to_cell)
1088 : END IF
1089 13422 : ALLOCATE (kpoint%index_to_cell(3, ncount))
1090 4474 : index_to_cell => kpoint%index_to_cell
1091 316898 : DO ic = 1, ncount
1092 257825248 : cell = MINLOC(cti)
1093 312424 : i1 = cell(1) - 1 - itm(1)
1094 312424 : i2 = cell(2) - 1 - itm(2)
1095 312424 : i3 = cell(3) - 1 - itm(3)
1096 312424 : cti(i1, i2, i3) = 1000000
1097 312424 : index_to_cell(1, ic) = i1
1098 312424 : index_to_cell(2, ic) = i2
1099 316898 : index_to_cell(3, ic) = i3
1100 : END DO
1101 731680 : cti(:, :, :) = 0
1102 316898 : DO ic = 1, ncount
1103 312424 : i1 = index_to_cell(1, ic)
1104 312424 : i2 = index_to_cell(2, ic)
1105 312424 : i3 = index_to_cell(3, ic)
1106 316898 : cti(i1, i2, i3) = ic
1107 : END DO
1108 :
1109 : ! keep pointer to this neighborlist
1110 4474 : kpoint%sab_nl => sab_nl
1111 :
1112 : ! set number of images
1113 4474 : nimages = SIZE(index_to_cell, 2)
1114 :
1115 4474 : DEALLOCATE (list)
1116 :
1117 4474 : CALL timestop(handle)
1118 :
1119 4474 : END SUBROUTINE kpoint_init_cell_index
1120 :
1121 : ! **************************************************************************************************
1122 : !> \brief Transformation of real space matrices to a kpoint
1123 : !> \param rmatrix Real part of kpoint matrix
1124 : !> \param cmatrix Complex part of kpoint matrix (optional)
1125 : !> \param rsmat Real space matrices
1126 : !> \param ispin Spin index
1127 : !> \param xkp Kpoint coordinates
1128 : !> \param cell_to_index mapping of cell indices to RS index
1129 : !> \param sab_nl Defining neighbor list
1130 : !> \param is_complex Matrix to be transformed is imaginary
1131 : !> \param rs_sign Matrix to be transformed is csaled by rs_sign
1132 : ! **************************************************************************************************
1133 57652 : SUBROUTINE rskp_transform(rmatrix, cmatrix, rsmat, ispin, &
1134 : xkp, cell_to_index, sab_nl, is_complex, rs_sign)
1135 :
1136 : TYPE(dbcsr_type) :: rmatrix
1137 : TYPE(dbcsr_type), OPTIONAL :: cmatrix
1138 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: rsmat
1139 : INTEGER, INTENT(IN) :: ispin
1140 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
1141 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
1142 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1143 : POINTER :: sab_nl
1144 : LOGICAL, INTENT(IN), OPTIONAL :: is_complex
1145 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: rs_sign
1146 :
1147 : CHARACTER(LEN=*), PARAMETER :: routineN = 'rskp_transform'
1148 :
1149 : INTEGER :: handle, iatom, ic, icol, irow, jatom, &
1150 : nimg
1151 : INTEGER, DIMENSION(3) :: cell
1152 : LOGICAL :: do_symmetric, found, my_complex, &
1153 : wfn_real_only
1154 : REAL(KIND=dp) :: arg, coskl, fsign, fsym, sinkl
1155 28826 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: cblock, rblock, rsblock
1156 : TYPE(neighbor_list_iterator_p_type), &
1157 28826 : DIMENSION(:), POINTER :: nl_iterator
1158 :
1159 28826 : CALL timeset(routineN, handle)
1160 :
1161 28826 : my_complex = .FALSE.
1162 28826 : IF (PRESENT(is_complex)) my_complex = is_complex
1163 :
1164 28826 : fsign = 1.0_dp
1165 28826 : IF (PRESENT(rs_sign)) fsign = rs_sign
1166 :
1167 28826 : wfn_real_only = .TRUE.
1168 28826 : IF (PRESENT(cmatrix)) wfn_real_only = .FALSE.
1169 :
1170 28826 : nimg = SIZE(rsmat, 2)
1171 :
1172 28826 : CALL get_neighbor_list_set_p(neighbor_list_sets=sab_nl, symmetric=do_symmetric)
1173 :
1174 28826 : CALL neighbor_list_iterator_create(nl_iterator, sab_nl)
1175 15779756 : DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
1176 15750930 : CALL get_iterator_info(nl_iterator, iatom=iatom, jatom=jatom, cell=cell)
1177 :
1178 : ! fsym = +- 1 is due to real space matrices being non-symmetric (although in a symmtric type)
1179 : ! with the link S_mu^0,nu^b = S_nu^0,mu^-b, and the KP matrices beeing Hermitian
1180 15750930 : fsym = 1.0_dp
1181 15750930 : irow = iatom
1182 15750930 : icol = jatom
1183 15750930 : IF (do_symmetric .AND. (iatom > jatom)) THEN
1184 6320830 : irow = jatom
1185 6320830 : icol = iatom
1186 6320830 : fsym = -1.0_dp
1187 : END IF
1188 :
1189 15750930 : ic = cell_to_index(cell(1), cell(2), cell(3))
1190 15750930 : IF (ic < 1 .OR. ic > nimg) CYCLE
1191 :
1192 15750186 : arg = REAL(cell(1), dp)*xkp(1) + REAL(cell(2), dp)*xkp(2) + REAL(cell(3), dp)*xkp(3)
1193 15750186 : IF (my_complex) THEN
1194 0 : coskl = fsign*fsym*COS(twopi*arg)
1195 0 : sinkl = fsign*SIN(twopi*arg)
1196 : ELSE
1197 15750186 : coskl = fsign*COS(twopi*arg)
1198 15750186 : sinkl = fsign*fsym*SIN(twopi*arg)
1199 : END IF
1200 :
1201 : CALL dbcsr_get_block_p(matrix=rsmat(ispin, ic)%matrix, row=irow, col=icol, &
1202 15750186 : block=rsblock, found=found)
1203 15750186 : IF (.NOT. found) CYCLE
1204 :
1205 15779012 : IF (wfn_real_only) THEN
1206 : CALL dbcsr_get_block_p(matrix=rmatrix, row=irow, col=icol, &
1207 94896 : block=rblock, found=found)
1208 94896 : IF (.NOT. found) CYCLE
1209 91246320 : rblock = rblock + coskl*rsblock
1210 : ELSE
1211 : CALL dbcsr_get_block_p(matrix=rmatrix, row=irow, col=icol, &
1212 15655290 : block=rblock, found=found)
1213 15655290 : IF (.NOT. found) CYCLE
1214 : CALL dbcsr_get_block_p(matrix=cmatrix, row=irow, col=icol, &
1215 15655290 : block=cblock, found=found)
1216 15655290 : IF (.NOT. found) CYCLE
1217 1948446498 : rblock = rblock + coskl*rsblock
1218 1948446498 : cblock = cblock + sinkl*rsblock
1219 : END IF
1220 :
1221 : END DO
1222 28826 : CALL neighbor_list_iterator_release(nl_iterator)
1223 :
1224 28826 : CALL timestop(handle)
1225 :
1226 28826 : END SUBROUTINE rskp_transform
1227 :
1228 : ! **************************************************************************************************
1229 : !> \brief Prepare a batched real-cell to complete reciprocal-grid transform for local DBCSR blocks.
1230 : !> The stored blocks remain MPI local. Irregular or too-large grids return used_fft=.FALSE.
1231 : !> \param grid cached transformed local blocks and their DBCSR coordinates
1232 : !> \param rmatrix allocated output template defining the local block layout
1233 : !> \param rsmat real-space matrix set
1234 : !> \param ispin spin component of rsmat
1235 : !> \param xkp complete reciprocal grid in arbitrary order
1236 : !> \param nkp_grid reciprocal grid dimensions
1237 : !> \param cell_to_index real-cell coordinate mapping
1238 : !> \param sab_nl neighbor list defining the stored block orientation
1239 : !> \param used_fft whether the cache was prepared
1240 : !> \param is_complex whether the real-space operator is imaginary
1241 : !> \param rs_sign optional overall sign
1242 : !> \param max_storage_bytes optional conservative per-rank memory limit
1243 : ! **************************************************************************************************
1244 0 : SUBROUTINE rskp_transform_grid_prepare(grid, rmatrix, rsmat, ispin, xkp, nkp_grid, &
1245 : cell_to_index, sab_nl, used_fft, is_complex, rs_sign, &
1246 : max_storage_bytes)
1247 :
1248 : TYPE(rskp_grid_type), INTENT(INOUT) :: grid
1249 : TYPE(dbcsr_type), INTENT(INOUT) :: rmatrix
1250 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: rsmat
1251 : INTEGER, INTENT(IN) :: ispin
1252 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: xkp
1253 : INTEGER, DIMENSION(3), INTENT(IN) :: nkp_grid
1254 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
1255 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1256 : POINTER :: sab_nl
1257 : LOGICAL, INTENT(OUT) :: used_fft
1258 : LOGICAL, INTENT(IN), OPTIONAL :: is_complex
1259 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: rs_sign
1260 : INTEGER(KIND=int_8), INTENT(IN), OPTIONAL :: max_storage_bytes
1261 :
1262 : CHARACTER(LEN=*), PARAMETER :: routineN = 'rskp_transform_grid_prepare'
1263 :
1264 : INTEGER :: handle, i1, i2, i3, iatom, iblock, ic, &
1265 : icol, irow, jatom, nblkcols, nblkrows, &
1266 : nblocks, ncell, nelem, nimg, nkp, &
1267 : nvalues
1268 : INTEGER(KIND=int_8) :: memory_limit, storage_bytes
1269 0 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: block_map, index_to_cell
1270 0 : INTEGER, ALLOCATABLE, DIMENSION(:, :, :) :: fft_cell_to_index
1271 : INTEGER, DIMENSION(3) :: cell, fft_cell
1272 : LOGICAL :: do_symmetric, found, my_complex
1273 : REAL(KIND=dp) :: fsign, fsym, value_sign
1274 0 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: values_rs
1275 0 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: rblock, rsblock
1276 : TYPE(dbcsr_iterator_type) :: iter
1277 : TYPE(neighbor_list_iterator_p_type), &
1278 0 : DIMENSION(:), POINTER :: nl_iterator
1279 :
1280 0 : CALL timeset(routineN, handle)
1281 0 : CALL rskp_transform_grid_release(grid)
1282 0 : used_fft = .FALSE.
1283 :
1284 0 : nkp = SIZE(xkp, 2)
1285 0 : nimg = SIZE(rsmat, 2)
1286 0 : IF (nkp < 2 .OR. PRODUCT(nkp_grid) /= nkp) THEN
1287 0 : CALL timestop(handle)
1288 0 : RETURN
1289 : END IF
1290 0 : IF (.NOT. regular_kpoint_grid(xkp, nkp_grid)) THEN
1291 0 : CALL timestop(handle)
1292 0 : RETURN
1293 : END IF
1294 :
1295 0 : nblocks = 0
1296 0 : nvalues = 0
1297 0 : CALL dbcsr_iterator_start(iter, rmatrix)
1298 0 : DO WHILE (dbcsr_iterator_blocks_left(iter))
1299 0 : CALL dbcsr_iterator_next_block(iter, irow, icol, rblock)
1300 0 : nblocks = nblocks + 1
1301 0 : nvalues = nvalues + SIZE(rblock)
1302 : END DO
1303 0 : CALL dbcsr_iterator_stop(iter)
1304 0 : IF (nblocks == 0 .OR. nvalues == 0) THEN
1305 0 : CALL timestop(handle)
1306 0 : RETURN
1307 : END IF
1308 :
1309 0 : CALL dbcsr_get_info(rmatrix, nblkrows_total=nblkrows, nblkcols_total=nblkcols)
1310 0 : ALLOCATE (block_map(nblkrows, nblkcols), source=0)
1311 0 : ALLOCATE (grid%block_row(nblocks), grid%block_col(nblocks), &
1312 0 : grid%block_offset(nblocks), grid%block_nelem(nblocks))
1313 0 : iblock = 0
1314 0 : nelem = 0
1315 0 : CALL dbcsr_iterator_start(iter, rmatrix)
1316 0 : DO WHILE (dbcsr_iterator_blocks_left(iter))
1317 0 : CALL dbcsr_iterator_next_block(iter, irow, icol, rblock)
1318 0 : iblock = iblock + 1
1319 0 : grid%block_row(iblock) = irow
1320 0 : grid%block_col(iblock) = icol
1321 0 : grid%block_offset(iblock) = nelem + 1
1322 0 : grid%block_nelem(iblock) = SIZE(rblock)
1323 0 : block_map(irow, icol) = iblock
1324 0 : nelem = nelem + SIZE(rblock)
1325 : END DO
1326 0 : CALL dbcsr_iterator_stop(iter)
1327 :
1328 : ALLOCATE (fft_cell_to_index(LBOUND(cell_to_index, 1):UBOUND(cell_to_index, 1), &
1329 : LBOUND(cell_to_index, 2):UBOUND(cell_to_index, 2), &
1330 0 : LBOUND(cell_to_index, 3):UBOUND(cell_to_index, 3)), source=0)
1331 0 : ncell = 0
1332 0 : DO i3 = LBOUND(cell_to_index, 3), UBOUND(cell_to_index, 3)
1333 0 : DO i2 = LBOUND(cell_to_index, 2), UBOUND(cell_to_index, 2)
1334 0 : DO i1 = LBOUND(cell_to_index, 1), UBOUND(cell_to_index, 1)
1335 0 : IF (cell_to_index(i1, i2, i3) < 1 .OR. cell_to_index(i1, i2, i3) > nimg) CYCLE
1336 0 : IF (fft_cell_to_index(i1, i2, i3) == 0) THEN
1337 0 : ncell = ncell + 1
1338 0 : fft_cell_to_index(i1, i2, i3) = ncell
1339 : END IF
1340 0 : IF (fft_cell_to_index(-i1, -i2, -i3) == 0) THEN
1341 0 : ncell = ncell + 1
1342 0 : fft_cell_to_index(-i1, -i2, -i3) = ncell
1343 : END IF
1344 : END DO
1345 : END DO
1346 : END DO
1347 0 : IF (ncell == 0) THEN
1348 0 : CALL rskp_transform_grid_release(grid)
1349 0 : CALL timestop(handle)
1350 0 : RETURN
1351 : END IF
1352 :
1353 0 : memory_limit = 512_int_8*1024_int_8**2
1354 0 : IF (PRESENT(max_storage_bytes)) memory_limit = max_storage_bytes
1355 : ! Include the real-cell input, complex result and conservative FFT work/padding allowance.
1356 : storage_bytes = INT(nvalues, int_8)*(8_int_8*INT(ncell, int_8) + &
1357 0 : 80_int_8*INT(nkp, int_8))
1358 0 : IF (storage_bytes > memory_limit) THEN
1359 0 : CALL rskp_transform_grid_release(grid)
1360 0 : CALL timestop(handle)
1361 0 : RETURN
1362 : END IF
1363 :
1364 0 : ALLOCATE (index_to_cell(3, ncell), source=0)
1365 0 : DO i3 = LBOUND(fft_cell_to_index, 3), UBOUND(fft_cell_to_index, 3)
1366 0 : DO i2 = LBOUND(fft_cell_to_index, 2), UBOUND(fft_cell_to_index, 2)
1367 0 : DO i1 = LBOUND(fft_cell_to_index, 1), UBOUND(fft_cell_to_index, 1)
1368 0 : ic = fft_cell_to_index(i1, i2, i3)
1369 0 : IF (ic > 0) index_to_cell(:, ic) = [i1, i2, i3]
1370 : END DO
1371 : END DO
1372 : END DO
1373 :
1374 0 : ALLOCATE (values_rs(nvalues, 1, ncell), source=0.0_dp)
1375 0 : ALLOCATE (grid%values(nvalues, 1, nkp))
1376 0 : my_complex = .FALSE.
1377 0 : IF (PRESENT(is_complex)) my_complex = is_complex
1378 0 : fsign = 1.0_dp
1379 0 : IF (PRESENT(rs_sign)) fsign = rs_sign
1380 :
1381 0 : CALL get_neighbor_list_set_p(neighbor_list_sets=sab_nl, symmetric=do_symmetric)
1382 0 : CALL neighbor_list_iterator_create(nl_iterator, sab_nl)
1383 0 : DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
1384 0 : CALL get_iterator_info(nl_iterator, iatom=iatom, jatom=jatom, cell=cell)
1385 :
1386 0 : fsym = 1.0_dp
1387 0 : irow = iatom
1388 0 : icol = jatom
1389 0 : IF (do_symmetric .AND. iatom > jatom) THEN
1390 0 : irow = jatom
1391 0 : icol = iatom
1392 0 : fsym = -1.0_dp
1393 : END IF
1394 0 : IF (irow < 1 .OR. irow > nblkrows .OR. icol < 1 .OR. icol > nblkcols) CYCLE
1395 0 : iblock = block_map(irow, icol)
1396 0 : IF (iblock == 0) CYCLE
1397 :
1398 0 : ic = cell_to_index(cell(1), cell(2), cell(3))
1399 0 : IF (ic < 1 .OR. ic > nimg) CYCLE
1400 : CALL dbcsr_get_block_p(matrix=rsmat(ispin, ic)%matrix, row=irow, col=icol, &
1401 0 : block=rsblock, found=found)
1402 0 : IF (.NOT. found) CYCLE
1403 :
1404 0 : fft_cell = cell
1405 0 : value_sign = fsign
1406 0 : IF (fsym < 0.0_dp) fft_cell = -cell
1407 0 : IF (my_complex) value_sign = value_sign*fsym
1408 0 : ic = fft_cell_to_index(fft_cell(1), fft_cell(2), fft_cell(3))
1409 0 : nelem = grid%block_nelem(iblock)
1410 0 : i1 = grid%block_offset(iblock)
1411 : values_rs(i1:i1 + nelem - 1, 1, ic) = values_rs(i1:i1 + nelem - 1, 1, ic) + &
1412 0 : value_sign*RESHAPE(rsblock, [nelem])
1413 : END DO
1414 0 : CALL neighbor_list_iterator_release(nl_iterator)
1415 :
1416 0 : CALL cell_to_k_grid_fft(values_rs, index_to_cell, xkp, nkp_grid, grid%values, used_fft)
1417 0 : IF (used_fft) THEN
1418 0 : grid%ready = .TRUE.
1419 : ELSE
1420 0 : CALL rskp_transform_grid_release(grid)
1421 : END IF
1422 :
1423 0 : CALL timestop(handle)
1424 :
1425 0 : END SUBROUTINE rskp_transform_grid_prepare
1426 :
1427 : ! **************************************************************************************************
1428 : !> \brief Extract one reciprocal-grid matrix from a prepared local DBCSR block cache.
1429 : !> \param grid ...
1430 : !> \param ikp ...
1431 : !> \param rmatrix ...
1432 : !> \param cmatrix ...
1433 : ! **************************************************************************************************
1434 0 : SUBROUTINE rskp_transform_grid_extract(grid, ikp, rmatrix, cmatrix)
1435 :
1436 : TYPE(rskp_grid_type), INTENT(IN) :: grid
1437 : INTEGER, INTENT(IN) :: ikp
1438 : TYPE(dbcsr_type), INTENT(INOUT) :: rmatrix
1439 : TYPE(dbcsr_type), INTENT(INOUT), OPTIONAL :: cmatrix
1440 :
1441 : INTEGER :: iblock, ioffset, nelem
1442 : LOGICAL :: found
1443 0 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: cblock, rblock
1444 :
1445 0 : CPASSERT(grid%ready)
1446 0 : CPASSERT(ikp >= 1 .AND. ikp <= SIZE(grid%values, 3))
1447 0 : CALL dbcsr_set(rmatrix, 0.0_dp)
1448 0 : IF (PRESENT(cmatrix)) CALL dbcsr_set(cmatrix, 0.0_dp)
1449 :
1450 0 : DO iblock = 1, SIZE(grid%block_row)
1451 0 : ioffset = grid%block_offset(iblock)
1452 0 : nelem = grid%block_nelem(iblock)
1453 : CALL dbcsr_get_block_p(rmatrix, grid%block_row(iblock), grid%block_col(iblock), &
1454 0 : rblock, found=found)
1455 0 : CPASSERT(found)
1456 : rblock = RESHAPE(REAL(grid%values(ioffset:ioffset + nelem - 1, 1, ikp), KIND=dp), &
1457 0 : SHAPE(rblock))
1458 0 : IF (PRESENT(cmatrix)) THEN
1459 : CALL dbcsr_get_block_p(cmatrix, grid%block_row(iblock), grid%block_col(iblock), &
1460 0 : cblock, found=found)
1461 0 : CPASSERT(found)
1462 0 : cblock = RESHAPE(AIMAG(grid%values(ioffset:ioffset + nelem - 1, 1, ikp)), SHAPE(cblock))
1463 : END IF
1464 : END DO
1465 :
1466 0 : END SUBROUTINE rskp_transform_grid_extract
1467 :
1468 : ! **************************************************************************************************
1469 : !> \brief Release a reciprocal-grid transformation cache.
1470 : !> \param grid ...
1471 : ! **************************************************************************************************
1472 146744 : SUBROUTINE rskp_transform_grid_release(grid)
1473 :
1474 : TYPE(rskp_grid_type), INTENT(INOUT) :: grid
1475 :
1476 146744 : IF (ALLOCATED(grid%values)) DEALLOCATE (grid%values)
1477 146744 : IF (ALLOCATED(grid%block_row)) DEALLOCATE (grid%block_row)
1478 146744 : IF (ALLOCATED(grid%block_col)) DEALLOCATE (grid%block_col)
1479 146744 : IF (ALLOCATED(grid%block_offset)) DEALLOCATE (grid%block_offset)
1480 146744 : IF (ALLOCATED(grid%block_nelem)) DEALLOCATE (grid%block_nelem)
1481 146744 : grid%ready = .FALSE.
1482 :
1483 146744 : END SUBROUTINE rskp_transform_grid_release
1484 :
1485 : ! **************************************************************************************************
1486 : !> \brief Given the eigenvalues of all kpoints, calculates the occupation numbers
1487 : !> \param kpoint Kpoint environment
1488 : !> \param smear Smearing information
1489 : !> \param probe ...
1490 : !> \param added_mos_auto ...
1491 : !> \param added_mos_auto_grow ...
1492 : !> \param separate_spin_occupations enforce the electron count of each spin channel
1493 : ! **************************************************************************************************
1494 38840 : SUBROUTINE kpoint_set_mo_occupation( &
1495 38840 : kpoint, smear, probe, added_mos_auto, added_mos_auto_grow, separate_spin_occupations)
1496 :
1497 : TYPE(kpoint_type), POINTER :: kpoint
1498 : TYPE(smear_type) :: smear
1499 : TYPE(hairy_probes_type), DIMENSION(:), OPTIONAL, &
1500 : POINTER :: probe
1501 : LOGICAL, DIMENSION(:), INTENT(IN), OPTIONAL :: added_mos_auto
1502 : LOGICAL, INTENT(OUT), OPTIONAL :: added_mos_auto_grow
1503 : LOGICAL, INTENT(IN), OPTIONAL :: separate_spin_occupations
1504 :
1505 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_set_mo_occupation'
1506 :
1507 38840 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: csmatrix
1508 : INTEGER :: handle, ik, ikpgr, ispin, kplocal, nao, &
1509 : nb, ncol_global, ne_a, ne_b, &
1510 : nelectron, nkp, nmo, nrow_global, nspin
1511 : INTEGER, DIMENSION(2) :: kp_range
1512 : LOGICAL :: my_added_mos_auto_grow, &
1513 : my_separate_spin_occupations
1514 : REAL(KIND=dp) :: kTS, kTS_spin(2), mu, mus(2), nel
1515 38840 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: smatrix
1516 38840 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: weig, wocc
1517 38840 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :, :) :: icoeff, rcoeff
1518 38840 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigenvalues, occupation, wkp
1519 : TYPE(cp_cfm_type), POINTER :: cmo_coeff
1520 : TYPE(cp_fm_type), POINTER :: mo_coeff
1521 : TYPE(kpoint_env_type), POINTER :: kp
1522 : TYPE(mo_set_type), POINTER :: mo_set
1523 : TYPE(mp_para_env_type), POINTER :: para_env_inter_kp
1524 :
1525 38840 : CALL timeset(routineN, handle)
1526 :
1527 38840 : my_added_mos_auto_grow = .FALSE.
1528 38840 : IF (PRESENT(added_mos_auto_grow)) added_mos_auto_grow = .FALSE.
1529 :
1530 : ! first collect all the eigenvalues
1531 38840 : CALL get_kpoint_info(kpoint, nkp=nkp)
1532 38840 : kp => kpoint%kp_env(1)%kpoint_env
1533 38840 : nspin = SIZE(kp%mos)
1534 38840 : my_separate_spin_occupations = smear%fixed_mag_mom > 0.0_dp
1535 38840 : IF (PRESENT(separate_spin_occupations)) THEN
1536 2418 : my_separate_spin_occupations = my_separate_spin_occupations .OR. separate_spin_occupations
1537 : END IF
1538 38840 : mo_set => kp%mos(1)
1539 38840 : CALL get_mo_set(mo_set, nmo=nmo, nao=nao, nelectron=nelectron)
1540 38840 : ne_a = nelectron
1541 38840 : IF (nspin == 2) THEN
1542 2654 : CALL get_mo_set(kp%mos(2), nmo=nb, nelectron=ne_b)
1543 2654 : CPASSERT(nmo == nb)
1544 : END IF
1545 310720 : ALLOCATE (weig(nmo, nkp, nspin), wocc(nmo, nkp, nspin))
1546 38840 : weig = 0.0_dp
1547 38840 : wocc = 0.0_dp
1548 38840 : IF (PRESENT(probe)) THEN
1549 0 : ALLOCATE (rcoeff(nao, nmo, nkp, nspin), icoeff(nao, nmo, nkp, nspin))
1550 0 : rcoeff = 0.0_dp !coeff, real part
1551 0 : icoeff = 0.0_dp !coeff, imaginary part
1552 : END IF
1553 38840 : CALL get_kpoint_info(kpoint, kp_range=kp_range)
1554 38840 : kplocal = kp_range(2) - kp_range(1) + 1
1555 110293 : DO ikpgr = 1, kplocal
1556 71453 : ik = kp_range(1) + ikpgr - 1
1557 71453 : kp => kpoint%kp_env(ikpgr)%kpoint_env
1558 189464 : DO ispin = 1, nspin
1559 79171 : mo_set => kp%mos(ispin)
1560 79171 : CALL get_mo_set(mo_set, eigenvalues=eigenvalues)
1561 1346741 : weig(1:nmo, ik, ispin) = eigenvalues(1:nmo)
1562 150624 : IF (PRESENT(probe)) THEN
1563 0 : IF (kpoint%use_real_wfn) THEN
1564 0 : CALL get_mo_set(mo_set, mo_coeff=mo_coeff)
1565 0 : CALL cp_fm_get_info(mo_coeff, nrow_global=nrow_global, ncol_global=ncol_global)
1566 0 : ALLOCATE (smatrix(nrow_global, ncol_global))
1567 0 : CALL cp_fm_get_submatrix(mo_coeff, smatrix)
1568 0 : rcoeff(1:nao, 1:nmo, ik, ispin) = smatrix(1:nao, 1:nmo)
1569 0 : DEALLOCATE (smatrix)
1570 : ELSE
1571 0 : CALL get_mo_set(mo_set, cmo_coeff=cmo_coeff)
1572 0 : CALL cp_cfm_get_info(cmo_coeff, nrow_global=nrow_global, ncol_global=ncol_global)
1573 0 : ALLOCATE (csmatrix(nrow_global, ncol_global))
1574 0 : CALL cp_cfm_get_submatrix(cmo_coeff, csmatrix)
1575 0 : rcoeff(1:nao, 1:nmo, ik, ispin) = REAL(csmatrix(1:nao, 1:nmo), KIND=dp)
1576 0 : icoeff(1:nao, 1:nmo, ik, ispin) = AIMAG(csmatrix(1:nao, 1:nmo))
1577 0 : DEALLOCATE (csmatrix)
1578 : END IF
1579 : END IF
1580 : END DO
1581 : END DO
1582 38840 : CALL get_kpoint_info(kpoint, para_env_inter_kp=para_env_inter_kp)
1583 38840 : CALL para_env_inter_kp%sum(weig)
1584 :
1585 38840 : IF (PRESENT(probe)) THEN
1586 0 : CALL para_env_inter_kp%sum(rcoeff)
1587 0 : CALL para_env_inter_kp%sum(icoeff)
1588 : END IF
1589 :
1590 38840 : CALL get_kpoint_info(kpoint, wkp=wkp)
1591 38840 : kTS_spin = 0.0_dp
1592 :
1593 : !calling of HP module HERE, before smear
1594 38840 : IF (PRESENT(probe)) THEN
1595 0 : smear%do_smear = .FALSE. !ensures smearing is switched off
1596 :
1597 0 : IF (nspin == 1) THEN
1598 0 : nel = REAL(nelectron, KIND=dp)
1599 : CALL probe_occupancy_kp(wocc(:, :, :), mus(1), kTS, weig(:, :, :), rcoeff(:, :, :, :), icoeff(:, :, :, :), 2.0d0, &
1600 0 : probe, nel, wkp)
1601 : ELSE
1602 0 : nel = REAL(ne_a, KIND=dp) + REAL(ne_b, KIND=dp)
1603 : CALL probe_occupancy_kp(wocc(:, :, :), mu, kTS, weig(:, :, :), rcoeff(:, :, :, :), icoeff(:, :, :, :), 1.0d0, &
1604 0 : probe, nel, wkp)
1605 0 : kTS = kTS/2._dp
1606 0 : mus(1:2) = mu
1607 : END IF
1608 :
1609 0 : DO ikpgr = 1, kplocal
1610 0 : ik = kp_range(1) + ikpgr - 1
1611 0 : kp => kpoint%kp_env(ikpgr)%kpoint_env
1612 0 : DO ispin = 1, nspin
1613 0 : mo_set => kp%mos(ispin)
1614 0 : CALL get_mo_set(mo_set, eigenvalues=eigenvalues, occupation_numbers=occupation)
1615 0 : eigenvalues(1:nmo) = weig(1:nmo, ik, ispin)
1616 0 : occupation(1:nmo) = wocc(1:nmo, ik, ispin)
1617 0 : mo_set%kTS = kTS
1618 0 : mo_set%mu = mus(ispin)
1619 :
1620 : END DO
1621 : END DO
1622 :
1623 0 : DEALLOCATE (weig, wocc, rcoeff, icoeff)
1624 :
1625 : END IF
1626 :
1627 : IF (PRESENT(probe) .EQV. .FALSE.) THEN
1628 38840 : IF (smear%do_smear) THEN
1629 28124 : SELECT CASE (smear%method)
1630 : CASE (smear_fermi_dirac)
1631 : ! finite electronic temperature
1632 13920 : IF (nspin == 1) THEN
1633 13326 : nel = REAL(nelectron, KIND=dp)
1634 : CALL Smearkp(wocc(:, :, 1), mus(1), kTS, weig(:, :, 1), nel, wkp, &
1635 13326 : smear%electronic_temperature, 2.0_dp, smear_fermi_dirac)
1636 13326 : kTS_spin(1) = kTS
1637 594 : ELSE IF (my_separate_spin_occupations) THEN
1638 104 : nel = REAL(ne_a, KIND=dp)
1639 : CALL Smearkp(wocc(:, :, 1), mus(1), kTS, weig(:, :, 1), nel, wkp, &
1640 104 : smear%electronic_temperature, 1.0_dp, smear_fermi_dirac)
1641 104 : kTS_spin(1) = kTS
1642 104 : nel = REAL(ne_b, KIND=dp)
1643 : CALL Smearkp(wocc(:, :, 2), mus(2), kTS, weig(:, :, 2), nel, wkp, &
1644 104 : smear%electronic_temperature, 1.0_dp, smear_fermi_dirac)
1645 104 : kTS_spin(2) = kTS
1646 : ELSE
1647 490 : nel = REAL(ne_a, KIND=dp) + REAL(ne_b, KIND=dp)
1648 : CALL Smearkp2(wocc(:, :, :), mu, kTS, weig(:, :, :), nel, wkp, &
1649 490 : smear%electronic_temperature, smear_fermi_dirac)
1650 490 : kTS = kTS/2._dp
1651 1470 : kTS_spin(1:2) = kTS
1652 1470 : mus(1:2) = mu
1653 : END IF
1654 : CASE (smear_gaussian, smear_mp, smear_mv)
1655 284 : IF (nspin == 1) THEN
1656 252 : nel = REAL(nelectron, KIND=dp)
1657 : CALL Smearkp(wocc(:, :, 1), mus(1), kTS, weig(:, :, 1), nel, wkp, &
1658 252 : smear%smearing_width, 2.0_dp, smear%method)
1659 252 : kTS_spin(1) = kTS
1660 32 : ELSE IF (my_separate_spin_occupations) THEN
1661 0 : nel = REAL(ne_a, KIND=dp)
1662 : CALL Smearkp(wocc(:, :, 1), mus(1), kTS, weig(:, :, 1), nel, wkp, &
1663 0 : smear%smearing_width, 1.0_dp, smear%method)
1664 0 : kTS_spin(1) = kTS
1665 0 : nel = REAL(ne_b, KIND=dp)
1666 : CALL Smearkp(wocc(:, :, 2), mus(2), kTS, weig(:, :, 2), nel, wkp, &
1667 0 : smear%smearing_width, 1.0_dp, smear%method)
1668 0 : kTS_spin(2) = kTS
1669 : ELSE
1670 32 : nel = REAL(ne_a, KIND=dp) + REAL(ne_b, KIND=dp)
1671 : CALL Smearkp2(wocc(:, :, :), mu, kTS, weig(:, :, :), nel, wkp, &
1672 32 : smear%smearing_width, smear%method)
1673 32 : kTS = kTS/2._dp
1674 96 : kTS_spin(1:2) = kTS
1675 96 : mus(1:2) = mu
1676 : END IF
1677 : CASE DEFAULT
1678 14204 : CPABORT("kpoints: Selected smearing not (yet) supported")
1679 : END SELECT
1680 : ELSE
1681 : ! fixed occupations (2/1)
1682 24636 : IF (nspin == 1) THEN
1683 22608 : nel = REAL(nelectron, KIND=dp)
1684 : CALL Smearkp(wocc(:, :, 1), mus(1), kTS, weig(:, :, 1), nel, wkp, &
1685 22608 : 0.0_dp, 2.0_dp, smear_gaussian)
1686 22608 : kTS_spin(1) = kTS
1687 : ELSE
1688 2028 : nel = REAL(ne_a, KIND=dp)
1689 : CALL Smearkp(wocc(:, :, 1), mus(1), kTS, weig(:, :, 1), nel, wkp, &
1690 2028 : 0.0_dp, 1.0_dp, smear_gaussian)
1691 2028 : kTS_spin(1) = kTS
1692 2028 : nel = REAL(ne_b, KIND=dp)
1693 : CALL Smearkp(wocc(:, :, 2), mus(2), kTS, weig(:, :, 2), nel, wkp, &
1694 2028 : 0.0_dp, 1.0_dp, smear_gaussian)
1695 2028 : kTS_spin(2) = kTS
1696 : END IF
1697 : END IF
1698 38840 : IF (smear%do_smear .AND. PRESENT(added_mos_auto)) THEN
1699 : CALL kpoint_check_added_mos_auto_occupation( &
1700 13916 : wocc, wkp, smear, nspin, added_mos_auto, nao, my_added_mos_auto_grow)
1701 : END IF
1702 38840 : IF (my_added_mos_auto_grow) THEN
1703 10 : IF (PRESENT(added_mos_auto_grow)) THEN
1704 10 : added_mos_auto_grow = .TRUE.
1705 10 : DEALLOCATE (weig, wocc)
1706 10 : CALL timestop(handle)
1707 10 : RETURN
1708 : ELSE
1709 : CALL cp_abort(__LOCATION__, &
1710 : "K-point ADDED_MOS AUTO needs a larger virtual-space buffer, "// &
1711 0 : "but this call path cannot grow it.")
1712 : END IF
1713 : END IF
1714 110271 : DO ikpgr = 1, kplocal
1715 71441 : ik = kp_range(1) + ikpgr - 1
1716 71441 : kp => kpoint%kp_env(ikpgr)%kpoint_env
1717 189426 : DO ispin = 1, nspin
1718 79155 : mo_set => kp%mos(ispin)
1719 79155 : CALL get_mo_set(mo_set, eigenvalues=eigenvalues, occupation_numbers=occupation)
1720 1346605 : eigenvalues(1:nmo) = weig(1:nmo, ik, ispin)
1721 1346605 : occupation(1:nmo) = wocc(1:nmo, ik, ispin)
1722 79155 : mo_set%kTS = kTS_spin(ispin)
1723 79155 : mo_set%mu = mus(ispin)
1724 150596 : IF (.NOT. smear%do_smear) THEN
1725 : ! Zero-width filling can give different occupied ranks at different k points.
1726 523671 : kp%mos(ispin)%homo = COUNT(occupation(1:nmo) > 0.0_dp)
1727 : END IF
1728 : END DO
1729 : END DO
1730 :
1731 38830 : DEALLOCATE (weig, wocc)
1732 :
1733 : END IF
1734 :
1735 38830 : CALL timestop(handle)
1736 :
1737 116520 : END SUBROUTINE kpoint_set_mo_occupation
1738 :
1739 : ! **************************************************************************************************
1740 : !> \brief summarize whether weighted k-point smearing reaches the available band edges
1741 : !> \param wocc ...
1742 : !> \param wkp ...
1743 : !> \param smear ...
1744 : !> \param nspin ...
1745 : !> \param has_weight ...
1746 : !> \param first_fractional ...
1747 : !> \param last_occupied ...
1748 : !> \param last_occupied_spin ...
1749 : ! **************************************************************************************************
1750 750 : SUBROUTINE kpoint_smearing_edge_status(wocc, wkp, smear, nspin, has_weight, &
1751 750 : first_fractional, last_occupied, last_occupied_spin)
1752 :
1753 : REAL(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: wocc
1754 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: wkp
1755 : TYPE(smear_type), INTENT(IN) :: smear
1756 : INTEGER, INTENT(IN) :: nspin
1757 : LOGICAL, INTENT(OUT) :: has_weight, first_fractional, &
1758 : last_occupied
1759 : LOGICAL, DIMENSION(:), INTENT(OUT), OPTIONAL :: last_occupied_spin
1760 :
1761 : INTEGER :: ik, ispin, nmo
1762 : LOGICAL :: band_occupied
1763 : REAL(KIND=dp) :: eps_occ, maxocc, weight, weight_threshold
1764 :
1765 750 : has_weight = .FALSE.
1766 750 : first_fractional = .FALSE.
1767 750 : last_occupied = .FALSE.
1768 1602 : IF (PRESENT(last_occupied_spin)) last_occupied_spin(:) = .FALSE.
1769 750 : IF (.NOT. smear%do_smear) RETURN
1770 :
1771 748 : nmo = SIZE(wocc, 1)
1772 748 : IF (nmo < 1) RETURN
1773 748 : eps_occ = MAX(smear%eps_fermi_dirac, 10.0_dp*EPSILON(1.0_dp))
1774 748 : weight_threshold = 10.0_dp*EPSILON(1.0_dp)
1775 :
1776 1598 : DO ispin = 1, MIN(nspin, SIZE(wocc, 3))
1777 850 : maxocc = MERGE(2.0_dp, 1.0_dp, nspin == 1)
1778 3116 : DO ik = 1, MIN(SIZE(wkp), SIZE(wocc, 2))
1779 1518 : weight = ABS(wkp(ik))
1780 1518 : IF (weight <= weight_threshold) CYCLE
1781 1506 : has_weight = .TRUE.
1782 : first_fractional = first_fractional .OR. &
1783 1506 : ABS(wocc(1, ik, ispin) - maxocc) > eps_occ
1784 1506 : band_occupied = ABS(wocc(nmo, ik, ispin)) > eps_occ
1785 1506 : last_occupied = last_occupied .OR. band_occupied
1786 2356 : IF (band_occupied .AND. PRESENT(last_occupied_spin)) THEN
1787 16 : IF (ispin <= SIZE(last_occupied_spin)) last_occupied_spin(ispin) = .TRUE.
1788 : END IF
1789 : END DO
1790 : END DO
1791 :
1792 : END SUBROUTINE kpoint_smearing_edge_status
1793 :
1794 : ! **************************************************************************************************
1795 : !> \brief request ADDED_MOS AUTO growth if weighted k-point smearing occupies the last band
1796 : !> \param wocc ...
1797 : !> \param wkp ...
1798 : !> \param smear ...
1799 : !> \param nspin ...
1800 : !> \param added_mos_auto ...
1801 : !> \param nao ...
1802 : !> \param added_mos_auto_grow ...
1803 : ! **************************************************************************************************
1804 13916 : SUBROUTINE kpoint_check_added_mos_auto_occupation(wocc, wkp, smear, nspin, &
1805 13916 : added_mos_auto, nao, added_mos_auto_grow)
1806 :
1807 : REAL(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: wocc
1808 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: wkp
1809 : TYPE(smear_type), INTENT(IN) :: smear
1810 : INTEGER, INTENT(IN) :: nspin
1811 : LOGICAL, DIMENSION(:), INTENT(IN) :: added_mos_auto
1812 : INTEGER, INTENT(IN) :: nao
1813 : LOGICAL, INTENT(OUT) :: added_mos_auto_grow
1814 :
1815 : INTEGER :: ispin, nmo
1816 : LOGICAL :: first_fractional, has_weight, &
1817 : last_occupied
1818 13916 : LOGICAL, DIMENSION(nspin) :: last_occupied_spin
1819 :
1820 13916 : added_mos_auto_grow = .FALSE.
1821 40268 : IF (.NOT. ANY(added_mos_auto) .OR. .NOT. smear%do_smear) RETURN
1822 :
1823 : CALL kpoint_smearing_edge_status(wocc, wkp, smear, nspin, has_weight, &
1824 : first_fractional, last_occupied, &
1825 740 : last_occupied_spin=last_occupied_spin)
1826 740 : IF (.NOT. has_weight) RETURN
1827 :
1828 740 : nmo = SIZE(wocc, 1)
1829 1562 : DO ispin = 1, MIN(nspin, SIZE(added_mos_auto))
1830 832 : IF (.NOT. added_mos_auto(ispin) .OR. .NOT. last_occupied_spin(ispin)) CYCLE
1831 10 : IF (nmo >= nao) THEN
1832 : CALL cp_abort(__LOCATION__, &
1833 : "K-point ADDED_MOS AUTO exhausted the AO basis but the highest band "// &
1834 0 : "is still occupied. Use a larger basis or reduce the smearing width.")
1835 : END IF
1836 10 : added_mos_auto_grow = .TRUE.
1837 1552 : RETURN
1838 : END DO
1839 :
1840 : END SUBROUTINE kpoint_check_added_mos_auto_occupation
1841 :
1842 : ! **************************************************************************************************
1843 : !> \brief Calculate kpoint density matrices (rho(k), owned by kpoint groups)
1844 : !> \param kpoint kpoint environment
1845 : !> \param energy_weighted calculate energy weighted density matrix
1846 : !> \param for_aux_fit ...
1847 : ! **************************************************************************************************
1848 41074 : SUBROUTINE kpoint_density_matrices(kpoint, energy_weighted, for_aux_fit)
1849 :
1850 : TYPE(kpoint_type), POINTER :: kpoint
1851 : LOGICAL, OPTIONAL :: energy_weighted, for_aux_fit
1852 :
1853 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_density_matrices'
1854 :
1855 : INTEGER :: handle, ikpgr, ispin, kplocal, nao, nmo, &
1856 : nspin, nworkers, thread
1857 : LOGICAL :: aux_fit, local, wtype
1858 41074 : TYPE(cp_cfm_type), ALLOCATABLE :: cdensity(:), cwork(:)
1859 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct
1860 41074 : TYPE(cp_fm_type), ALLOCATABLE :: work(:)
1861 : TYPE(kpoint_env_type), POINTER :: kp
1862 41074 : TYPE(local_gemm_ctxt_type), ALLOCATABLE :: gemm_ctx(:)
1863 :
1864 41074 : CALL timeset(routineN, handle)
1865 41074 : wtype = .FALSE.
1866 41074 : IF (PRESENT(energy_weighted)) wtype = energy_weighted
1867 41074 : aux_fit = .FALSE.
1868 41074 : IF (PRESENT(for_aux_fit)) aux_fit = for_aux_fit
1869 41074 : IF (aux_fit) THEN
1870 122 : CPASSERT(ASSOCIATED(kpoint%kp_aux_env))
1871 122 : kp => kpoint%kp_aux_env(1)%kpoint_env
1872 : ELSE
1873 40952 : kp => kpoint%kp_env(1)%kpoint_env
1874 : END IF
1875 41074 : CALL get_mo_set(kp%mos(1), nao=nao, nmo=nmo)
1876 41074 : IF (kpoint%use_real_wfn) THEN
1877 140 : CALL cp_fm_get_info(kp%mos(1)%mo_coeff, matrix_struct=matrix_struct)
1878 : ELSE
1879 40934 : CALL cp_cfm_get_info(kp%mos(1)%cmo_coeff, matrix_struct=matrix_struct)
1880 : END IF
1881 41074 : kplocal = kpoint%kp_range(2) - kpoint%kp_range(1) + 1
1882 41074 : nspin = SIZE(kp%mos)
1883 : ! Only the group layout matters. Distributed GEMM stays on the calling thread.
1884 123222 : local = PRODUCT(matrix_struct%context%num_pe) == 1
1885 41074 : nworkers = 1
1886 41074 : !$ IF (local) nworkers = MIN(omp_get_max_threads(), kplocal*nspin)
1887 164296 : ALLOCATE (gemm_ctx(nworkers))
1888 41074 : IF (kpoint%use_real_wfn) THEN
1889 560 : ALLOCATE (work(nworkers))
1890 : ELSE
1891 245604 : ALLOCATE (cwork(nworkers), cdensity(nworkers))
1892 : END IF
1893 82148 : DO thread = 1, nworkers
1894 41074 : IF (kpoint%use_real_wfn) THEN
1895 140 : CALL cp_fm_create(work(thread), matrix_struct)
1896 : ELSE
1897 40934 : CALL cp_cfm_create(cwork(thread), matrix_struct)
1898 40934 : CALL cp_cfm_create(cdensity(thread), kp%pmat(1, 1)%matrix_struct)
1899 : END IF
1900 82148 : IF (local) CALL gemm_ctx(thread)%create(LOCAL_GEMM_PU_GPU, timing=.FALSE.)
1901 : END DO
1902 :
1903 : !$OMP PARALLEL DO COLLAPSE(2) DEFAULT(NONE) SCHEDULE(DYNAMIC, 1) NUM_THREADS(nworkers) IF(nworkers > 1) &
1904 41074 : !$OMP SHARED(kpoint,wtype,aux_fit,nao,nmo,kplocal,nspin,nworkers,work,cwork,cdensity,local,gemm_ctx) PRIVATE(ikpgr,ispin,thread)
1905 : DO ikpgr = 1, kplocal
1906 : DO ispin = 1, nspin
1907 : thread = 1
1908 : !$ thread = omp_get_thread_num() + 1
1909 : IF (kpoint%use_real_wfn) THEN
1910 : CALL kpoint_density_matrix_job(kpoint, ikpgr, ispin, wtype, aux_fit, nao, nmo, &
1911 : local, gemm_ctx(thread), work=work(thread))
1912 : ELSE
1913 : CALL kpoint_density_matrix_job(kpoint, ikpgr, ispin, wtype, aux_fit, nao, nmo, &
1914 : local, gemm_ctx(thread), cwork=cwork(thread), cdensity=cdensity(thread))
1915 : END IF
1916 : END DO
1917 : END DO
1918 : !$OMP END PARALLEL DO
1919 82148 : DO thread = 1, nworkers
1920 41074 : CALL gemm_ctx(thread)%destroy()
1921 82148 : IF (kpoint%use_real_wfn) THEN
1922 140 : CALL cp_fm_release(work(thread))
1923 : ELSE
1924 40934 : CALL cp_cfm_release(cwork(thread))
1925 40934 : CALL cp_cfm_release(cdensity(thread))
1926 : END IF
1927 : END DO
1928 41074 : IF (.NOT. wtype .AND. .NOT. aux_fit) THEN
1929 40492 : kpoint%lowdin_density_ready = .TRUE.
1930 40492 : kpoint%lowdin_population_ready = .FALSE.
1931 : END IF
1932 41074 : CALL timestop(handle)
1933 :
1934 123222 : END SUBROUTINE kpoint_density_matrices
1935 :
1936 : ! **************************************************************************************************
1937 : !> \brief Build W(k) for noncanonical complex OT orbitals from H(k) C(k).
1938 : !> The occupied-space Lagrange multiplier is
1939 : !> Hermitian[(C^H H C) f], which reduces to the usual eigenvalue-weighted
1940 : !> expression for canonical orbitals.
1941 : !> \param coeff C(k)
1942 : !> \param hc H(k) C(k)
1943 : !> \param occupation orbital occupations
1944 : !> \param wmat W(k)
1945 : ! **************************************************************************************************
1946 82 : SUBROUTINE kpoint_ot_energy_weighted_density(coeff, hc, occupation, wmat)
1947 :
1948 : TYPE(cp_cfm_type), INTENT(IN) :: coeff, hc
1949 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: occupation
1950 : TYPE(cp_cfm_type), INTENT(INOUT) :: wmat
1951 :
1952 : COMPLEX(KIND=dp), PARAMETER :: zhalf = (0.5_dp, 0.0_dp), &
1953 : zone = (1.0_dp, 0.0_dp), &
1954 : zzero = (0.0_dp, 0.0_dp)
1955 :
1956 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:) :: occupation_complex
1957 : INTEGER :: nao, nmo
1958 : TYPE(cp_cfm_type) :: hblock, hblock_h, weighted
1959 :
1960 82 : CALL cp_cfm_get_info(coeff, nrow_global=nao, ncol_global=nmo)
1961 82 : CPASSERT(nmo >= 1)
1962 82 : CPASSERT(SIZE(occupation) >= nmo)
1963 :
1964 82 : CALL cp_cfm_create(weighted, coeff%matrix_struct)
1965 82 : CALL cp_cfm_create(hblock, coeff%matrix_struct, nrow=nmo, ncol=nmo)
1966 82 : CALL cp_cfm_create(hblock_h, coeff%matrix_struct, nrow=nmo, ncol=nmo)
1967 82 : CALL parallel_gemm("C", "N", nmo, nmo, nao, zone, coeff, hc, zzero, hblock)
1968 :
1969 246 : ALLOCATE (occupation_complex(nmo))
1970 412 : occupation_complex(:) = CMPLX(occupation(1:nmo), 0.0_dp, KIND=dp)
1971 82 : CALL cp_cfm_column_scale(hblock, occupation_complex)
1972 82 : DEALLOCATE (occupation_complex)
1973 82 : CALL cp_cfm_transpose(hblock, "C", hblock_h)
1974 82 : CALL cp_cfm_scale_and_add(zhalf, hblock, zhalf, hblock_h)
1975 :
1976 82 : CALL parallel_gemm("N", "N", nao, nmo, nmo, zone, coeff, hblock, zzero, weighted)
1977 82 : CALL parallel_gemm("N", "C", nao, nao, nmo, zone, weighted, coeff, zzero, wmat)
1978 :
1979 82 : CALL cp_cfm_release(hblock_h)
1980 82 : CALL cp_cfm_release(hblock)
1981 82 : CALL cp_cfm_release(weighted)
1982 :
1983 164 : END SUBROUTINE kpoint_ot_energy_weighted_density
1984 :
1985 : ! **************************************************************************************************
1986 : !> \brief Common C-to-P algebra for local and distributed k-point groups.
1987 : !> \param kpoint host environment
1988 : !> \param ikpgr local k-point index
1989 : !> \param ispin spin index
1990 : !> \param wtype construct the energy-weighted density
1991 : !> \param aux_fit use the auxiliary MO set
1992 : !> \param nao number of atomic orbitals
1993 : !> \param nmo number of molecular orbitals
1994 : !> \param local use local GEMM, without communication
1995 : !> \param gemm_ctx private local GEMM context, unused for distributed groups
1996 : !> \param work real column-scaled MO scratch
1997 : !> \param cwork complex column-scaled MO scratch
1998 : !> \param cdensity complex density scratch
1999 : ! **************************************************************************************************
2000 85762 : SUBROUTINE kpoint_density_matrix_job(kpoint, ikpgr, ispin, wtype, aux_fit, nao, nmo, &
2001 : local, gemm_ctx, work, cwork, cdensity)
2002 :
2003 : TYPE(kpoint_type), POINTER :: kpoint
2004 : INTEGER, INTENT(IN) :: ikpgr, ispin
2005 : LOGICAL, INTENT(IN) :: wtype, aux_fit
2006 : INTEGER, INTENT(IN) :: nao, nmo
2007 : LOGICAL, INTENT(IN) :: local
2008 : TYPE(local_gemm_ctxt_type), INTENT(INOUT) :: gemm_ctx
2009 : TYPE(cp_fm_type), INTENT(INOUT), OPTIONAL :: work
2010 : TYPE(cp_cfm_type), INTENT(INOUT), OPTIONAL :: cwork, cdensity
2011 :
2012 171524 : COMPLEX(KIND=dp) :: weights(nmo)
2013 85762 : REAL(KIND=dp), POINTER :: eigenvalues(:), occupation(:)
2014 : TYPE(cp_cfm_type), POINTER :: ccoeff
2015 : TYPE(cp_fm_type), POINTER :: coeff, cpmat, rpmat
2016 : TYPE(kpoint_env_type), POINTER :: kp
2017 :
2018 85762 : IF (aux_fit) THEN
2019 1138 : kp => kpoint%kp_aux_env(ikpgr)%kpoint_env
2020 : ELSE
2021 84624 : kp => kpoint%kp_env(ikpgr)%kpoint_env
2022 : END IF
2023 85762 : CALL get_mo_set(kp%mos(ispin), occupation_numbers=occupation, eigenvalues=eigenvalues)
2024 85762 : IF (wtype) THEN
2025 1529 : rpmat => kp%wmat(1, ispin)
2026 : ELSE
2027 84233 : rpmat => kp%pmat(1, ispin)
2028 : END IF
2029 85762 : IF (kpoint%use_real_wfn) THEN
2030 168 : CPASSERT(PRESENT(work))
2031 168 : coeff => kp%mos(ispin)%mo_coeff
2032 168 : CALL cp_fm_to_fm(coeff, work)
2033 168 : CALL cp_fm_column_scale(work, occupation)
2034 168 : IF (wtype) CALL cp_fm_column_scale(work, eigenvalues)
2035 168 : IF (local) THEN
2036 0 : CPASSERT(PRODUCT(coeff%matrix_struct%context%num_pe) == 1)
2037 0 : CPASSERT(PRODUCT(work%matrix_struct%context%num_pe) == 1)
2038 0 : CPASSERT(PRODUCT(rpmat%matrix_struct%context%num_pe) == 1)
2039 : CALL gemm_ctx%gemm("N", "T", nao, nao, nmo, 1.0_dp, &
2040 : coeff%local_data, SIZE(coeff%local_data, 1), &
2041 : work%local_data, SIZE(work%local_data, 1), 0.0_dp, &
2042 0 : rpmat%local_data, SIZE(rpmat%local_data, 1))
2043 : ELSE
2044 168 : CALL parallel_gemm("N", "T", nao, nao, nmo, 1.0_dp, coeff, work, 0.0_dp, rpmat)
2045 : END IF
2046 : ELSE
2047 85594 : CPASSERT(PRESENT(cwork) .AND. PRESENT(cdensity))
2048 85594 : IF (wtype) THEN
2049 1517 : cpmat => kp%wmat(2, ispin)
2050 : ELSE
2051 84077 : cpmat => kp%pmat(2, ispin)
2052 : END IF
2053 85594 : ccoeff => kp%mos(ispin)%cmo_coeff
2054 1412840 : weights = CMPLX(occupation(1:nmo), 0.0_dp, KIND=dp)
2055 119677 : IF (wtype) weights = weights*eigenvalues(1:nmo)
2056 85594 : CALL cp_cfm_to_cfm(ccoeff, cwork)
2057 85594 : CALL cp_cfm_column_scale(cwork, weights)
2058 85594 : IF (local) THEN
2059 171018 : CPASSERT(PRODUCT(ccoeff%matrix_struct%context%num_pe) == 1)
2060 : CALL gemm_ctx%gemm("N", "C", nao, nao, nmo, z_one, &
2061 : ccoeff%local_data, SIZE(ccoeff%local_data, 1), &
2062 : cwork%local_data, SIZE(cwork%local_data, 1), z_zero, &
2063 57006 : cdensity%local_data, SIZE(cdensity%local_data, 1))
2064 : ELSE
2065 28588 : CALL parallel_gemm("N", "C", nao, nao, nmo, z_one, ccoeff, cwork, z_zero, cdensity)
2066 : END IF
2067 85594 : CALL cp_cfm_to_fm(cdensity, rpmat, cpmat)
2068 : END IF
2069 :
2070 85762 : END SUBROUTINE kpoint_density_matrix_job
2071 :
2072 : ! **************************************************************************************************
2073 : !> \brief Calculate Lowdin transformation of density matrix S^1/2 P S^1/2
2074 : !> Integrate diagonal elements over k-points to get Lowdin charges
2075 : !> \param kpoint kpoint environment
2076 : !> \param pmat_diag Sum over kpoints of diagonal elements
2077 : !> \par History
2078 : !> 04.2026 created [JGH]
2079 : ! **************************************************************************************************
2080 12 : SUBROUTINE lowdin_kp_trans(kpoint, pmat_diag)
2081 :
2082 : TYPE(kpoint_type), POINTER :: kpoint
2083 : REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: pmat_diag
2084 :
2085 : CHARACTER(LEN=*), PARAMETER :: routineN = 'lowdin_kp_trans'
2086 : COMPLEX(KIND=dp), PARAMETER :: cone = (1.0_dp, 0.0_dp), &
2087 : czero = (0.0_dp, 0.0_dp)
2088 :
2089 : INTEGER :: handle, ikpgr, ispin, kplocal, nao, nspin
2090 : INTEGER, DIMENSION(2) :: kp_range
2091 12 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: dele
2092 : TYPE(cp_cfm_type) :: cf1work, cf2work
2093 : TYPE(cp_cfm_type), POINTER :: cshalf
2094 : TYPE(cp_fm_struct_type), POINTER :: matrix_struct
2095 : TYPE(cp_fm_type) :: f1work, f2work
2096 : TYPE(cp_fm_type), POINTER :: cpmat, pmat, rpmat, shalf
2097 : TYPE(kpoint_env_type), POINTER :: kp
2098 : TYPE(mp_para_env_type), POINTER :: para_env_inter_kp
2099 :
2100 12 : CALL timeset(routineN, handle)
2101 :
2102 12 : nspin = SIZE(pmat_diag, 2)
2103 822 : pmat_diag = 0.0_dp
2104 :
2105 : ! work matrix
2106 : CALL cp_fm_get_info(kpoint%kp_env(1)%kpoint_env%pmat(1, 1), &
2107 12 : matrix_struct=matrix_struct, nrow_global=nao)
2108 12 : IF (kpoint%use_real_wfn) THEN
2109 0 : CALL cp_fm_create(f1work, matrix_struct, nrow=nao, ncol=nao)
2110 0 : CALL cp_fm_create(f2work, matrix_struct, nrow=nao, ncol=nao)
2111 : ELSE
2112 12 : CALL cp_fm_create(f2work, matrix_struct, nrow=nao, ncol=nao)
2113 12 : CALL cp_cfm_create(cf1work, matrix_struct, nrow=nao, ncol=nao)
2114 12 : CALL cp_cfm_create(cf2work, matrix_struct, nrow=nao, ncol=nao)
2115 : END IF
2116 36 : ALLOCATE (dele(nao))
2117 :
2118 12 : CALL get_kpoint_info(kpoint, kp_range=kp_range)
2119 12 : kplocal = kp_range(2) - kp_range(1) + 1
2120 268 : DO ikpgr = 1, kplocal
2121 256 : kp => kpoint%kp_env(ikpgr)%kpoint_env
2122 564 : DO ispin = 1, nspin
2123 296 : IF (kpoint%use_real_wfn) THEN
2124 0 : pmat => kp%pmat(1, ispin)
2125 0 : shalf => kp%shalf
2126 0 : CALL parallel_gemm("N", "N", nao, nao, nao, 1.0_dp, pmat, shalf, 0.0_dp, f1work)
2127 0 : CALL parallel_gemm("N", "N", nao, nao, nao, 1.0_dp, shalf, f1work, 0.0_dp, f2work)
2128 : ELSE
2129 296 : rpmat => kp%pmat(1, ispin)
2130 296 : cpmat => kp%pmat(2, ispin)
2131 296 : cshalf => kp%cshalf
2132 296 : CALL cp_fm_to_cfm(rpmat, cpmat, cf1work)
2133 296 : CALL parallel_gemm("N", "N", nao, nao, nao, cone, cf1work, cshalf, czero, cf2work)
2134 296 : CALL parallel_gemm("N", "N", nao, nao, nao, cone, cshalf, cf2work, czero, cf1work)
2135 296 : CALL cp_cfm_to_fm(cf1work, mtargetr=f2work)
2136 : END IF
2137 296 : CALL cp_fm_get_diag(f2work, dele)
2138 4536 : pmat_diag(1:nao, ispin) = pmat_diag(1:nao, ispin) + kp%wkp*dele(1:nao)
2139 : END DO
2140 : END DO
2141 :
2142 12 : CALL get_kpoint_info(kpoint, para_env_inter_kp=para_env_inter_kp)
2143 1632 : CALL para_env_inter_kp%sum(pmat_diag)
2144 :
2145 12 : IF (kpoint%use_real_wfn) THEN
2146 0 : CALL cp_fm_release(f1work)
2147 0 : CALL cp_fm_release(f2work)
2148 : ELSE
2149 12 : CALL cp_fm_release(f2work)
2150 12 : CALL cp_cfm_release(cf1work)
2151 12 : CALL cp_cfm_release(cf2work)
2152 : END IF
2153 12 : DEALLOCATE (dele)
2154 :
2155 12 : CALL timestop(handle)
2156 :
2157 24 : END SUBROUTINE lowdin_kp_trans
2158 :
2159 : ! **************************************************************************************************
2160 : !> \brief Calculate S(k)^1/2 C(k) for real or complex k-point wavefunctions
2161 : !> \param kp K-point environment for one local k point
2162 : !> \param ispin Spin index
2163 : !> \param use_real_wfn Use real k-point wavefunctions
2164 : !> \param shalfc Output matrix containing S(k)^1/2 C(k) for real wavefunctions
2165 : !> \param cshalfc Output matrix containing S(k)^1/2 C(k) for complex wavefunctions
2166 : ! **************************************************************************************************
2167 0 : SUBROUTINE lowdin_kp_mo_coeff(kp, ispin, use_real_wfn, shalfc, cshalfc)
2168 :
2169 : TYPE(kpoint_env_type), POINTER :: kp
2170 : INTEGER, INTENT(IN) :: ispin
2171 : LOGICAL, INTENT(IN) :: use_real_wfn
2172 : TYPE(cp_fm_type), INTENT(INOUT), OPTIONAL :: shalfc
2173 : TYPE(cp_cfm_type), INTENT(INOUT), OPTIONAL :: cshalfc
2174 :
2175 : INTEGER :: nao, nmo
2176 : TYPE(mo_set_type), POINTER :: mo_set
2177 :
2178 0 : IF (use_real_wfn) THEN
2179 0 : CPASSERT(PRESENT(shalfc))
2180 0 : mo_set => kp%mos(ispin)
2181 0 : CALL get_mo_set(mo_set, nao=nao, nmo=nmo)
2182 :
2183 : CALL parallel_gemm("N", "N", nao, nmo, nao, 1.0_dp, kp%shalf, &
2184 0 : mo_set%mo_coeff, 0.0_dp, shalfc)
2185 : ELSE
2186 0 : CPASSERT(PRESENT(cshalfc))
2187 0 : mo_set => kp%mos(ispin)
2188 0 : CALL get_mo_set(mo_set, nao=nao, nmo=nmo)
2189 : CALL parallel_gemm("N", "N", nao, nmo, nao, z_one, kp%cshalf, &
2190 0 : mo_set%cmo_coeff, z_zero, cshalfc)
2191 : END IF
2192 :
2193 0 : END SUBROUTINE lowdin_kp_mo_coeff
2194 :
2195 : ! **************************************************************************************************
2196 : !> \brief generate real space density matrices in DBCSR format
2197 : !> \param kpoint Kpoint environment
2198 : !> \param denmat Real space (DBCSR) density matrices
2199 : !> \param wtype True = energy weighted density matrix
2200 : !> False = normal density matrix
2201 : !> \param tempmat DBCSR matrix to be used as template
2202 : !> \param sab_nl ...
2203 : !> \param fmwork FM work matrices (kpoint group)
2204 : !> \param for_aux_fit ...
2205 : !> \param pmat_ext ...
2206 : !> \param overlap_rs ...
2207 : ! **************************************************************************************************
2208 83048 : SUBROUTINE kpoint_density_transform(kpoint, denmat, wtype, tempmat, sab_nl, fmwork, for_aux_fit, &
2209 41524 : pmat_ext, overlap_rs)
2210 :
2211 : TYPE(kpoint_type), POINTER :: kpoint
2212 : TYPE(dbcsr_p_type), DIMENSION(:, :) :: denmat
2213 : LOGICAL, INTENT(IN) :: wtype
2214 : TYPE(dbcsr_type), POINTER :: tempmat
2215 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
2216 : POINTER :: sab_nl
2217 : TYPE(cp_fm_type), DIMENSION(:), INTENT(IN), TARGET :: fmwork
2218 : LOGICAL, OPTIONAL :: for_aux_fit
2219 : TYPE(cp_fm_type), DIMENSION(:, :, :), INTENT(IN), &
2220 : OPTIONAL, TARGET :: pmat_ext
2221 : TYPE(dbcsr_p_type), DIMENSION(:, :), OPTIONAL, &
2222 : POINTER :: overlap_rs
2223 :
2224 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_density_transform'
2225 :
2226 : INTEGER :: handle, ic, ik, ikp, ispin, kplocal, nc, &
2227 : nimg, nkp, nspin
2228 41524 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
2229 : LOGICAL :: aux_fit, do_symmetric, local, &
2230 : regular_grid_done, &
2231 : regular_grid_eligible
2232 : TYPE(cp_fm_p_type), ALLOCATABLE, &
2233 41524 : DIMENSION(:, :, :) :: source
2234 124572 : TYPE(dbcsr_p_type), DIMENSION(2) :: components
2235 : TYPE(dbcsr_type), POINTER :: cpmat, rpmat
2236 41524 : TYPE(kp_transform_plan_type) :: transform_plan
2237 : TYPE(kpoint_env_type), POINTER :: kp
2238 : TYPE(mp_para_env_type), POINTER :: para_env
2239 :
2240 41524 : CALL timeset(routineN, handle)
2241 :
2242 41524 : CALL get_neighbor_list_set_p(neighbor_list_sets=sab_nl, symmetric=do_symmetric)
2243 :
2244 41524 : IF (PRESENT(for_aux_fit)) THEN
2245 368 : aux_fit = for_aux_fit
2246 : ELSE
2247 : aux_fit = .FALSE.
2248 : END IF
2249 :
2250 368 : IF (aux_fit) THEN
2251 214 : CPASSERT(ASSOCIATED(kpoint%kp_aux_env))
2252 : END IF
2253 :
2254 : ! work storage
2255 41524 : ALLOCATE (rpmat)
2256 : CALL dbcsr_create(rpmat, template=tempmat, &
2257 41584 : matrix_type=MERGE(dbcsr_type_symmetric, dbcsr_type_no_symmetry, do_symmetric))
2258 41524 : CALL cp_dbcsr_alloc_block_from_nbl(rpmat, sab_nl)
2259 41524 : CALL dbcsr_set(rpmat, 0.0_dp)
2260 41524 : NULLIFY (cpmat)
2261 41524 : components(1)%matrix => rpmat
2262 :
2263 41524 : CALL get_kpoint_info(kpoint, nkp=nkp, cell_to_index=cell_to_index)
2264 41524 : IF (PRESENT(overlap_rs)) THEN
2265 40498 : CALL calibrate_symmetry_phases(kpoint, overlap_rs, tempmat, sab_nl, cell_to_index)
2266 : END IF
2267 : ! initialize real space density matrices
2268 41524 : IF (aux_fit) THEN
2269 214 : kp => kpoint%kp_aux_env(1)%kpoint_env
2270 : ELSE
2271 41310 : kp => kpoint%kp_env(1)%kpoint_env
2272 : END IF
2273 41524 : nspin = SIZE(kp%mos)
2274 41524 : nc = SIZE(kp%pmat, 1)
2275 41524 : nimg = SIZE(denmat, 2)
2276 : CALL kp_transform_plan_create(transform_plan, sab_nl, cell_to_index, nimg, &
2277 41524 : group_entries=.TRUE.)
2278 :
2279 : ! Borrow one common view for ordinary P, W, auxiliary and external matrices.
2280 : ! Pointers remain within this call, including the TARGET lifetime of pmat_ext.
2281 41524 : kplocal = kpoint%kp_range(2) - kpoint%kp_range(1) + 1
2282 517486 : ALLOCATE (source(kplocal, nc, nspin))
2283 86250 : DO ispin = 1, nspin
2284 174262 : DO ikp = 1, kplocal
2285 88012 : IF (aux_fit) THEN
2286 1725 : kp => kpoint%kp_aux_env(ikp)%kpoint_env
2287 : ELSE
2288 86287 : kp => kpoint%kp_env(ikp)%kpoint_env
2289 : END IF
2290 308594 : DO ic = 1, nc
2291 263868 : IF (PRESENT(pmat_ext)) THEN
2292 3926 : source(ikp, ic, ispin)%matrix => pmat_ext(ikp, ic, ispin)
2293 171930 : ELSE IF (wtype) THEN
2294 3202 : source(ikp, ic, ispin)%matrix => kp%wmat(ic, ispin)
2295 : ELSE
2296 168728 : source(ikp, ic, ispin)%matrix => kp%pmat(ic, ispin)
2297 : END IF
2298 : END DO
2299 : END DO
2300 : END DO
2301 :
2302 41524 : para_env => kpoint%blacs_env_all%para_env
2303 41524 : local = para_env%num_pe == 1
2304 41524 : IF (local) THEN
2305 0 : CPASSERT(kpoint%kp_range(1) == 1 .AND. kplocal == nkp)
2306 : END IF
2307 : ! The regular-grid route is an optional specialization without spatial rotations.
2308 : ! The batched block transform handles all remaining cases.
2309 41524 : regular_grid_eligible = nimg < nkp
2310 41524 : IF (ASSOCIATED(kpoint%index_to_cell)) THEN
2311 41524 : regular_grid_eligible = regular_grid_eligible .AND. nimg <= SIZE(kpoint%index_to_cell, 2)
2312 : ELSE
2313 : regular_grid_eligible = .FALSE.
2314 : END IF
2315 : IF (regular_grid_eligible) THEN
2316 3558 : DO ik = 1, nkp
2317 3558 : IF (kpoint%kp_sym(ik)%kpoint_sym%apply_symmetry) THEN
2318 : regular_grid_eligible = .FALSE.
2319 : EXIT
2320 : END IF
2321 : END DO
2322 : END IF
2323 :
2324 41486 : regular_grid_done = .FALSE.
2325 38 : IF (regular_grid_eligible) THEN
2326 38 : ALLOCATE (cpmat)
2327 : CALL dbcsr_create(cpmat, template=rpmat, &
2328 38 : matrix_type=MERGE(dbcsr_type_antisymmetric, dbcsr_type_no_symmetry, do_symmetric))
2329 38 : CALL cp_dbcsr_alloc_block_from_nbl(cpmat, sab_nl)
2330 38 : CALL dbcsr_set(cpmat, 0.0_dp)
2331 38 : components(2)%matrix => cpmat
2332 : CALL kpoint_density_transform_regular_grid(kpoint, source, denmat, components, fmwork, &
2333 38 : transform_plan, regular_grid_done)
2334 : END IF
2335 41524 : IF (.NOT. regular_grid_done) THEN
2336 41486 : CALL kpoint_density_transform_batched(kpoint, source, denmat, rpmat, transform_plan)
2337 : END IF
2338 :
2339 41524 : CALL dbcsr_deallocate_matrix(rpmat)
2340 41524 : IF (ASSOCIATED(cpmat)) CALL dbcsr_deallocate_matrix(cpmat)
2341 :
2342 41524 : CALL timestop(handle)
2343 :
2344 124572 : END SUBROUTINE kpoint_density_transform
2345 :
2346 : ! **************************************************************************************************
2347 : !> \brief Contract group-local densities into bounded batches of real-space AO blocks.
2348 : !> Small tiles of images sharing an AO block are independent OpenMP work items. Rotations follow the
2349 : !> k-point contraction; only the resulting blocks are reduced to their DBCSR
2350 : !> owner. This also works when a source AO block spans several MPI ranks.
2351 : !> \param kpoint ...
2352 : !> \param source borrowed P, W or external density matrices
2353 : !> \param denmat real-space output matrices
2354 : !> \param template union of the source AO sparsities
2355 : !> \param plan neighbor-list traversal and storage conventions
2356 : ! **************************************************************************************************
2357 41486 : SUBROUTINE kpoint_density_transform_batched(kpoint, source, denmat, template, plan)
2358 : TYPE(kpoint_type), POINTER :: kpoint
2359 : TYPE(cp_fm_p_type), DIMENSION(:, :, :), INTENT(IN) :: source
2360 : TYPE(dbcsr_p_type), DIMENSION(:, :) :: denmat
2361 : TYPE(dbcsr_type), POINTER :: template
2362 : TYPE(kp_transform_plan_type), INTENT(IN) :: plan
2363 :
2364 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_density_transform_batched'
2365 : INTEGER(KIND=int_8), PARAMETER :: max_bytes = 64_int_8*1024*1024
2366 : INTEGER, PARAMETER :: images_per_tile = 8, max_batch = 256
2367 :
2368 : INTEGER :: a, b, batch, first, handle, i, ic, igroup, ikp, image, ispin, j, last, nblock, &
2369 : ncol, njob, nlocal, nrow, nthreads, ntile, nvalue, orientation, owner, slot, thread, tile
2370 : INTEGER(KIND=int_8) :: block_size
2371 41486 : INTEGER, ALLOCATABLE :: available(:, :, :), counts(:), &
2372 41486 : jobs(:, :), local_jobs(:, :), &
2373 41486 : offsets(:), tile_start(:), &
2374 41486 : tiles(:, :, :, :, :)
2375 41486 : INTEGER, DIMENSION(:), POINTER :: col_offset, col_size, row_offset, &
2376 41486 : row_size
2377 : LOGICAL :: found, trans
2378 41486 : REAL(KIND=dp), ALLOCATABLE :: packed(:, :), phases(:, :), &
2379 41486 : rotation_work(:, :)
2380 41486 : REAL(KIND=dp), ALLOCATABLE, TARGET :: sums(:, :), values(:)
2381 41486 : REAL(KIND=dp), POINTER :: block(:, :)
2382 41486 : TYPE(kp_density_group_type), ALLOCATABLE :: groups(:)
2383 41486 : TYPE(local_gemm_ctxt_type), ALLOCATABLE :: gemm_ctx(:)
2384 : TYPE(mp_para_env_type), POINTER :: world
2385 :
2386 41486 : CALL timeset(routineN, handle)
2387 41486 : world => kpoint%blacs_env_all%para_env
2388 : CALL dbcsr_get_info(template, row_blk_size=row_size, col_blk_size=col_size, &
2389 41486 : row_blk_offset=row_offset, col_blk_offset=col_offset)
2390 41486 : CALL kp_density_groups_create(kpoint, SIZE(row_size), groups)
2391 41486 : CPASSERT(SIZE(row_size) == SIZE(col_size))
2392 248916 : ALLOCATE (tiles(4, SIZE(row_size), SIZE(source, 1), SIZE(source, 2), SIZE(source, 3)))
2393 86138 : DO ispin = 1, SIZE(source, 3)
2394 133788 : DO ic = 1, SIZE(source, 2)
2395 302604 : DO ikp = 1, SIZE(source, 1)
2396 : CALL kp_density_layout_create(source(ikp, ic, ispin)%matrix, row_size, col_size, &
2397 257952 : row_offset, col_offset, tiles(:, :, ikp, ic, ispin))
2398 : END DO
2399 : END DO
2400 2962316 : DO image = 1, SIZE(denmat, 2)
2401 2920830 : CALL dbcsr_set(denmat(ispin, image)%matrix, 0.0_dp)
2402 : END DO
2403 : END DO
2404 :
2405 : ! Only owners request blocks. A group contains one (image,row,col), hence
2406 : ! all its entries have the same cell; retain both multiplicity and sign.
2407 206015 : ALLOCATE (local_jobs(8, plan%ngroup), counts(0:world%num_pe - 1))
2408 41486 : nlocal = 0
2409 9626143 : DO igroup = 1, plan%ngroup
2410 9584657 : first = plan%group_start(igroup)
2411 9584657 : last = plan%group_start(igroup + 1) - 1
2412 : CALL dbcsr_get_readonly_block_p(denmat(1, plan%image(first))%matrix, &
2413 9584657 : plan%row(first), plan%col(first), block, found=found)
2414 9584657 : IF (.NOT. found) CYCLE
2415 9584657 : nlocal = nlocal + 1
2416 : local_jobs(:, nlocal) = [plan%row(first), plan%col(first), plan%image(first), &
2417 : plan%cell(:, first), last - first + 1, &
2418 134226684 : NINT(SUM(plan%symmetry_sign(first:last)))]
2419 : END DO
2420 41486 : CALL world%allgather(nlocal, counts)
2421 124458 : IF (MAXVAL(counts) == 0) THEN
2422 0 : CALL timestop(handle)
2423 : RETURN
2424 : END IF
2425 383306 : block_size = INT(MAXVAL(row_size), int_8)*MAXVAL(col_size)
2426 124458 : batch = MAX(1, MIN(max_batch, MAXVAL(counts), INT(max_bytes/(8_int_8*block_size))))
2427 0 : ALLOCATE (jobs(8, batch), offsets(batch + 1), tile_start(batch + 1), values(INT(block_size)*batch), &
2428 456346 : available(2, 0:UBOUND(groups, 1), batch))
2429 41486 : nthreads = 1
2430 41486 : !$ nthreads = MIN(omp_get_max_threads(), batch)
2431 165944 : ALLOCATE (gemm_ctx(nthreads))
2432 82972 : DO thread = 1, nthreads
2433 82972 : CALL gemm_ctx(thread)%create(LOCAL_GEMM_PU_HOST, timing=.FALSE.)
2434 : END DO
2435 :
2436 124458 : DO owner = 0, world%num_pe - 1
2437 258234 : DO first = 1, counts(owner), batch
2438 133776 : njob = MIN(batch, counts(owner) - first + 1)
2439 86395689 : IF (world%mepos == owner) jobs(:, 1:njob) = local_jobs(:, first:first + njob - 1)
2440 133776 : IF (world%num_pe > 1) CALL world%bcast(jobs(:, 1:njob), owner)
2441 133776 : offsets(1) = 1
2442 19303090 : DO j = 1, njob
2443 19303090 : offsets(j + 1) = offsets(j) + row_size(jobs(1, j))*col_size(jobs(2, j))
2444 : END DO
2445 : ! Neighbor-list groups are ordered by AO pair and image. Limit image
2446 : ! tiles to retain enough independent work items for small unit cells.
2447 : ntile = 0
2448 : i = 1
2449 2876358 : DO WHILE (i <= njob)
2450 2742582 : ntile = ntile + 1
2451 2742582 : tile_start(ntile) = i
2452 2742582 : j = i + 1
2453 19169314 : DO WHILE (j <= MIN(njob, i + images_per_tile - 1))
2454 49863828 : IF (ANY(jobs(1:2, j) /= jobs(1:2, i))) EXIT
2455 19169314 : j = j + 1
2456 : END DO
2457 133776 : i = j
2458 : END DO
2459 133776 : tile_start(ntile + 1) = njob + 1
2460 133776 : available(:, :, :) = 0
2461 2876358 : DO tile = 1, ntile
2462 2742582 : j = tile_start(tile)
2463 115736738 : DO slot = 0, UBOUND(groups, 1)
2464 110117798 : IF (.NOT. ALLOCATED(groups(slot)%inverse)) CYCLE
2465 36631608 : DO orientation = 1, 2
2466 : CALL kp_density_source_block(groups(slot), jobs(1:2, j), plan%symmetric, &
2467 22592684 : orientation, a, b, trans)
2468 22592684 : IF (a == 0) CYCLE
2469 11296342 : CALL dbcsr_get_readonly_block_p(template, a, b, block, found=found)
2470 138358653 : available(orientation, slot, tile) = MERGE(1, 0, found)
2471 : END DO
2472 : END DO
2473 : END DO
2474 : ! Source sparsity is distributed independently of the FM layout.
2475 : ! Reduce only presence flags, never the k-dependent source matrices.
2476 133776 : IF (world%num_pe > 1) CALL world%sum(available(:, :, 1:ntile))
2477 133776 : nvalue = offsets(njob + 1) - 1
2478 133776 : nthreads = MIN(SIZE(gemm_ctx), ntile)
2479 358730 : DO ispin = 1, SIZE(source, 3)
2480 1179274838 : values(1:nvalue) = 0.0_dp
2481 : !$OMP PARALLEL DEFAULT(NONE) NUM_THREADS(nthreads) &
2482 : !$OMP SHARED(source,tiles,groups,jobs,available,plan,row_size,col_size,row_offset,col_offset, &
2483 : !$OMP values,offsets,tile_start,ntile,ispin,gemm_ctx) &
2484 : !$OMP PRIVATE(tile,j,last,nrow,ncol,block,packed,phases,sums,rotation_work,thread)
2485 : thread = 1
2486 : !$ thread = omp_get_thread_num() + 1
2487 141982 : !$OMP DO SCHEDULE(DYNAMIC, 1)
2488 : DO tile = 1, ntile
2489 : j = tile_start(tile)
2490 : last = tile_start(tile + 1) - 1
2491 : nrow = row_size(jobs(1, j))
2492 : ncol = col_size(jobs(2, j))
2493 : block(1:nrow*ncol, 1:last - j + 1) => values(offsets(j):offsets(last + 1) - 1)
2494 : CALL kp_density_contract_tile(source(:, :, ispin), tiles(:, :, :, :, ispin), groups, &
2495 : jobs(:, j:last), available(:, :, tile), plan%symmetric, &
2496 : row_size, col_size, row_offset, col_offset, block, &
2497 : packed, phases, sums, rotation_work, gemm_ctx(thread))
2498 : END DO
2499 : !$OMP END DO
2500 : !$OMP END PARALLEL
2501 141982 : IF (world%num_pe > 1) CALL world%sum(values(1:nvalue), root=owner)
2502 141982 : IF (world%mepos /= owner) CYCLE
2503 10379079 : DO j = 1, njob
2504 10174312 : CALL dbcsr_get_block_p(denmat(ispin, jobs(3, j))%matrix, jobs(1, j), jobs(2, j), block, found=found)
2505 10174312 : CPASSERT(found)
2506 10174312 : nrow = SIZE(block, 1)
2507 85517684 : DO i = 1, SIZE(block, 2)
2508 65027078 : nblock = offsets(j) + (i - 1)*nrow
2509 1254334246 : block(:, i) = values(nblock:nblock + nrow - 1)
2510 : END DO
2511 : END DO
2512 : END DO
2513 : END DO
2514 : END DO
2515 82972 : DO thread = 1, SIZE(gemm_ctx)
2516 82972 : CALL gemm_ctx(thread)%destroy()
2517 : END DO
2518 41486 : CALL timestop(handle)
2519 915714 : END SUBROUTINE kpoint_density_transform_batched
2520 :
2521 : ! **************************************************************************************************
2522 : !> \brief Group the existing density contributions by operation and phase direction.
2523 : !> Only nwght entries contribute; additional little-group mappings are not
2524 : !> extra density terms. Metadata is borrowed, local k-point lists are owned.
2525 : !> \param kpoint ...
2526 : !> \param natom number of AO blocks
2527 : !> \param groups operation views and local contribution lists; zero is the identity
2528 : ! **************************************************************************************************
2529 41486 : SUBROUTINE kp_density_groups_create(kpoint, natom, groups)
2530 : TYPE(kpoint_type), POINTER :: kpoint
2531 : INTEGER, INTENT(IN) :: natom
2532 : TYPE(kp_density_group_type), ALLOCATABLE, &
2533 : INTENT(OUT) :: groups(:)
2534 :
2535 : INTEGER :: i, ik, ikp, is, ncontrib, nrot, pass, &
2536 : slot
2537 41486 : INTEGER, ALLOCATABLE :: counts(:)
2538 : TYPE(kpoint_sym_type), POINTER :: kpsym
2539 :
2540 41486 : nrot = 0
2541 41486 : IF (ASSOCIATED(kpoint%ibrot)) nrot = SIZE(kpoint%ibrot)
2542 1164630 : ALLOCATE (groups(0:4*nrot), counts(0:4*nrot))
2543 124458 : DO pass = 1, 2
2544 82972 : counts(:) = 0
2545 342700 : DO ik = 1, kpoint%nkp
2546 259728 : ikp = ik - kpoint%kp_range(1) + 1
2547 259728 : kpsym => kpoint%kp_sym(ik)%kpoint_sym
2548 259728 : ncontrib = 1
2549 259728 : IF (kpsym%apply_symmetry) ncontrib = kpsym%nwght
2550 808816 : DO is = 1, ncontrib
2551 466116 : slot = 0
2552 466116 : IF (kpsym%apply_symmetry) THEN
2553 271056 : CPASSERT(kpsym%phase_mode(is) >= 1 .AND. kpsym%phase_mode(is) <= 2)
2554 271056 : slot = find_kpoint_rotation_slot(kpoint, kpsym%rotp(is))
2555 271056 : CPASSERT(slot > 0)
2556 411180 : slot = slot + nrot*MERGE(1, 0, kpsym%rotp(is) < 0) + 2*nrot*(kpsym%phase_mode(is) - 1)
2557 : END IF
2558 466116 : IF (pass == 1) THEN
2559 233058 : IF (.NOT. ALLOCATED(groups(slot)%inverse)) THEN
2560 314292 : ALLOCATE (groups(slot)%inverse(natom))
2561 104764 : groups(slot)%op%identity = .TRUE.
2562 587822 : DO i = 1, natom
2563 587822 : groups(slot)%inverse(i) = i
2564 : END DO
2565 104764 : IF (slot > 0) THEN
2566 76334 : CALL kp_symmetry_op_init(groups(slot)%op, kpoint, kpsym, is)
2567 76334 : groups(slot)%reverse_phase = kpsym%phase_mode(is) == 2
2568 435328 : DO i = 1, natom
2569 435328 : groups(slot)%inverse(groups(slot)%op%atom_map(i)) = i
2570 : END DO
2571 : END IF
2572 128294 : ELSE IF (slot > 0) THEN
2573 : ! One crystallographic operation has one atom/gauge mapping.
2574 413236 : CPASSERT(ALL(groups(slot)%op%atom_map == kpsym%f0(:, is)))
2575 1475362 : CPASSERT(ALL(groups(slot)%op%cell_shift == kpsym%fcell_gauge(:, :, is)))
2576 : END IF
2577 : END IF
2578 466116 : IF (ik < kpoint%kp_range(1) .OR. ik > kpoint%kp_range(2)) CYCLE
2579 284256 : counts(slot) = counts(slot) + 1
2580 543984 : IF (pass == 2) THEN
2581 142128 : i = counts(slot)
2582 142128 : groups(slot)%ikp(i) = ikp
2583 142128 : groups(slot)%weight(i) = kpoint%wkp(ik)/REAL(ncontrib, dp)
2584 142128 : IF (slot == 0) THEN
2585 226696 : groups(slot)%xkp(:, i) = kpoint%xkp(:, ik)
2586 : ELSE
2587 341816 : groups(slot)%xkp(:, i) = kpsym%xkp(:, is)
2588 : END IF
2589 : END IF
2590 : END DO
2591 : END DO
2592 124458 : IF (pass == 1) THEN
2593 874228 : DO slot = 0, 4*nrot
2594 0 : ALLOCATE (groups(slot)%ikp(counts(slot)), groups(slot)%weight(counts(slot)), &
2595 3657214 : groups(slot)%xkp(3, counts(slot)))
2596 : END DO
2597 : END IF
2598 : END DO
2599 41486 : END SUBROUTINE kp_density_groups_create
2600 :
2601 : ! **************************************************************************************************
2602 : !> \brief Map each AO block to its local FM row and column intervals, excluding padding.
2603 : !> \param matrix ...
2604 : !> \param row_size ...
2605 : !> \param col_size ...
2606 : !> \param row_offset ...
2607 : !> \param col_offset ...
2608 : !> \param tile local first/last row and column indices for each atom
2609 : ! **************************************************************************************************
2610 168816 : SUBROUTINE kp_density_layout_create(matrix, row_size, col_size, row_offset, col_offset, tile)
2611 : TYPE(cp_fm_type), INTENT(IN) :: matrix
2612 : INTEGER, INTENT(IN) :: row_size(:), col_size(:), row_offset(:), &
2613 : col_offset(:)
2614 : INTEGER, INTENT(OUT) :: tile(:, :)
2615 :
2616 : INTEGER :: a, i, ncol, nrow
2617 168816 : INTEGER, ALLOCATABLE :: cols(:), rows(:)
2618 168816 : INTEGER, POINTER :: col_indices(:), row_indices(:)
2619 :
2620 876638 : CPASSERT(matrix%matrix_struct%nrow_global == SUM(row_size))
2621 876638 : CPASSERT(matrix%matrix_struct%ncol_global == SUM(col_size))
2622 : CALL cp_fm_get_info(matrix, nrow_local=nrow, ncol_local=ncol, &
2623 168816 : row_indices=row_indices, col_indices=col_indices)
2624 2428540 : ALLOCATE (rows(0:SUM(row_size)), cols(0:SUM(col_size)))
2625 168816 : rows(:) = 0
2626 168816 : cols(:) = 0
2627 3745341 : DO i = 1, nrow
2628 3745341 : rows(row_indices(i)) = 1
2629 : END DO
2630 4517246 : DO i = 1, ncol
2631 4517246 : cols(col_indices(i)) = 1
2632 : END DO
2633 4514686 : DO i = 1, UBOUND(rows, 1)
2634 4514686 : rows(i) = rows(i) + rows(i - 1)
2635 : END DO
2636 4517246 : DO i = 1, UBOUND(cols, 1)
2637 4517246 : cols(i) = cols(i) + cols(i - 1)
2638 : END DO
2639 876638 : DO a = 1, SIZE(row_size)
2640 2292282 : tile(1:2, a) = [rows(row_offset(a) - 1) + 1, rows(row_offset(a) + row_size(a) - 1)]
2641 : END DO
2642 876638 : DO a = 1, SIZE(col_size)
2643 2292282 : tile(3:4, a) = [cols(col_offset(a) - 1) + 1, cols(col_offset(a) + col_size(a) - 1)]
2644 : END DO
2645 506448 : END SUBROUTINE kp_density_layout_create
2646 :
2647 : ! **************************************************************************************************
2648 : !> \brief Find the source orientation that maps to a stored output block.
2649 : !> A nonsymmetric source can contribute both orientations to an upper block,
2650 : !> as in the DBCSR symmetry adapter. An absent orientation returns a=0.
2651 : !> \param group ...
2652 : !> \param TARGET output atom pair
2653 : !> \param symmetric source storage convention
2654 : !> \param orientation ...
2655 : !> \param a ...
2656 : !> \param b ...
2657 : !> \param trans whether rotation transposes the source block
2658 : ! **************************************************************************************************
2659 32293827 : SUBROUTINE kp_density_source_block(group, TARGET, symmetric, orientation, a, b, trans)
2660 : TYPE(kp_density_group_type), INTENT(IN) :: group
2661 : INTEGER, INTENT(IN) :: target(2)
2662 : LOGICAL, INTENT(IN) :: symmetric
2663 : INTEGER, INTENT(IN) :: orientation
2664 : INTEGER, INTENT(OUT) :: a, b
2665 : LOGICAL, INTENT(OUT) :: trans
2666 :
2667 : INTEGER :: i, j
2668 :
2669 32293827 : a = 0
2670 32293827 : b = 0
2671 32293827 : trans = .FALSE.
2672 32293827 : IF (group%op%identity) THEN
2673 9589780 : IF (orientation == 1) THEN
2674 6401934 : a = TARGET(1)
2675 6401934 : b = TARGET(2)
2676 : END IF
2677 9589780 : RETURN
2678 : END IF
2679 22704047 : IF (TARGET(1) > TARGET(2)) RETURN
2680 22704047 : i = group%inverse(TARGET(1))
2681 22704047 : j = group%inverse(TARGET(2))
2682 22704047 : IF (orientation == 2 .AND. (symmetric .OR. i == j)) RETURN
2683 14595551 : a = MIN(i, j)
2684 14595551 : b = MAX(i, j)
2685 14595551 : IF (orientation == 2) THEN
2686 0 : a = MAX(i, j)
2687 0 : b = MIN(i, j)
2688 : END IF
2689 14595551 : trans = group%op%atom_map(a) > group%op%atom_map(b)
2690 : END SUBROUTINE kp_density_source_block
2691 :
2692 : ! **************************************************************************************************
2693 : !> \brief Contract a small tile of images sharing an output AO block.
2694 : !> Pack bounded k-point batches once and reuse them across images in GEMM.
2695 : !> Gauge/Fourier phases remain inside the contraction; AO rotations follow
2696 : !> the completed local sum. MPI can then reduce the rotated partial blocks.
2697 : !> \param source ...
2698 : !> \param tiles local first/last row and column indices by atom, k-point and component
2699 : !> \param groups ...
2700 : !> \param jobs row, col, image, cell, real multiplicity and imaginary sign sum
2701 : !> \param available global source-block presence by orientation and operation
2702 : !> \param symmetric ...
2703 : !> \param row_size ...
2704 : !> \param col_size ...
2705 : !> \param row_offset ...
2706 : !> \param col_offset ...
2707 : !> \param TARGET flattened output blocks, one column per image
2708 : !> \param packed reusable thread-local source batch
2709 : !> \param phases reusable thread-local contraction coefficients
2710 : !> \param sums reusable thread-local contracted blocks
2711 : !> \param rotation_work reusable thread-local GEMM buffer
2712 : !> \param gemm_ctx thread-owned local GEMM context
2713 : ! **************************************************************************************************
2714 2903622 : SUBROUTINE kp_density_contract_tile(source, tiles, groups, jobs, available, symmetric, &
2715 2903622 : row_size, col_size, row_offset, col_offset, TARGET, &
2716 : packed, phases, sums, rotation_work, gemm_ctx)
2717 : TYPE(cp_fm_p_type), INTENT(IN) :: source(:, :)
2718 : INTEGER, INTENT(IN) :: tiles(:, :, :, :)
2719 : TYPE(kp_density_group_type), INTENT(IN) :: groups(0:)
2720 : INTEGER, INTENT(IN) :: jobs(:, :), available(:, 0:)
2721 : LOGICAL, INTENT(IN) :: symmetric
2722 : INTEGER, INTENT(IN) :: row_size(:), col_size(:), row_offset(:), &
2723 : col_offset(:)
2724 : REAL(KIND=dp), CONTIGUOUS, INTENT(INOUT), TARGET :: target(:, :)
2725 : REAL(KIND=dp), ALLOCATABLE, INTENT(INOUT) :: packed(:, :), phases(:, :)
2726 : REAL(KIND=dp), ALLOCATABLE, INTENT(INOUT), TARGET :: sums(:, :)
2727 : REAL(KIND=dp), ALLOCATABLE, INTENT(INOUT) :: rotation_work(:, :)
2728 : TYPE(local_gemm_ctxt_type), INTENT(INOUT) :: gemm_ctx
2729 :
2730 : INTEGER(KIND=int_8), PARAMETER :: max_elements = 65536_int_8
2731 :
2732 : INTEGER :: a, b, batch, column, first, i, ic, &
2733 : ifirst, ii, ikp, ilast, image, j, jj, &
2734 : k, last, nc, nelement, nimage, nrow, &
2735 : orientation, shift(3), slot
2736 : LOGICAL :: contributes, real_only, trans
2737 : REAL(KIND=dp) :: arg, cosine, factors(2, 2), sine
2738 : REAL(KIND=dp), POINTER :: left_rot(:, :), right_rot(:, :), &
2739 2903622 : source_block(:, :), target_block(:, :)
2740 : TYPE(cp_fm_type), POINTER :: matrix
2741 :
2742 : ! Up to 512 KiB of packed input per worker, except for a single larger AO block.
2743 :
2744 2903622 : nc = SIZE(source, 2)
2745 2903622 : real_only = nc == 1
2746 2903622 : nimage = SIZE(jobs, 2)
2747 115206940 : DO slot = 0, UBOUND(groups, 1)
2748 112303318 : IF (SIZE(groups(slot)%ikp) == 0) CYCLE
2749 32007051 : DO orientation = 1, 2
2750 19402286 : IF (available(orientation, slot) == 0) CYCLE
2751 9701143 : CALL kp_density_source_block(groups(slot), jobs(1:2, 1), symmetric, orientation, a, b, trans)
2752 9701143 : CPASSERT(a > 0)
2753 9701143 : nelement = row_size(a)*col_size(b)
2754 9701143 : batch = MAX(1, MIN(128, SIZE(groups(slot)%ikp), INT(max_elements/(INT(nelement, int_8)*nc))))
2755 9701143 : CALL ensure_work_matrix(packed, nelement, nc*batch)
2756 9701143 : CALL ensure_work_matrix(phases, nc*batch, nimage)
2757 9701143 : CALL ensure_work_matrix(sums, nelement, nimage)
2758 9701143 : sums(:, :) = 0.0_dp
2759 9701143 : shift(:) = 0
2760 9701143 : IF (.NOT. groups(slot)%op%identity) THEN
2761 25948220 : shift(:) = groups(slot)%op%cell_shift(:, b) - groups(slot)%op%cell_shift(:, a)
2762 19248203 : IF (groups(slot)%reverse_phase) shift(:) = -shift
2763 : END IF
2764 19402286 : DO first = 1, SIZE(groups(slot)%ikp), batch
2765 9701143 : last = MIN(first + batch - 1, SIZE(groups(slot)%ikp))
2766 9701143 : packed(:, :) = 0.0_dp
2767 9701143 : contributes = .FALSE.
2768 25998044 : DO k = first, last
2769 16296901 : ikp = groups(slot)%ikp(k)
2770 : CALL kp_symmetry_phase_factors(shift, groups(slot)%xkp(:, k), &
2771 16296901 : groups(slot)%op%time_reversal, real_only, factors)
2772 134365224 : DO image = 1, nimage
2773 472273292 : arg = twopi*DOT_PRODUCT(REAL(jobs(4:6, image), dp), groups(slot)%xkp(:, k))
2774 118068323 : cosine = REAL(jobs(7, image), dp)*COS(arg)
2775 118068323 : sine = REAL(jobs(8, image), dp)*SIN(arg)*MERGE(-1.0_dp, 1.0_dp, trans)
2776 118068323 : IF (real_only) sine = 0.0_dp
2777 369913644 : DO ic = 1, nc
2778 235548420 : column = (k - first)*nc + ic
2779 353616743 : phases(column, image) = groups(slot)%weight(k)*(cosine*factors(1, ic) + sine*factors(2, ic))
2780 : END DO
2781 : END DO
2782 58515420 : DO ic = 1, nc
2783 32517376 : ifirst = tiles(1, a, ikp, ic)
2784 32517376 : ilast = tiles(2, a, ikp, ic)
2785 32517376 : IF (ifirst > ilast .OR. tiles(3, b, ikp, ic) > tiles(4, b, ikp, ic)) CYCLE
2786 26535592 : contributes = .TRUE.
2787 26535592 : column = (k - first)*nc + ic
2788 26535592 : matrix => source(ikp, ic)%matrix
2789 26535592 : nrow = ilast - ifirst + 1
2790 228486552 : DO j = tiles(3, b, ikp, ic), tiles(4, b, ikp, ic)
2791 185654059 : jj = matrix%matrix_struct%col_indices(j) - col_offset(b)
2792 185654059 : ii = matrix%matrix_struct%row_indices(ifirst) - row_offset(a) + 1 + jj*row_size(a)
2793 185654059 : IF (matrix%matrix_struct%row_indices(ilast) - &
2794 32517376 : matrix%matrix_struct%row_indices(ifirst) == nrow - 1) THEN
2795 1767831310 : packed(ii:ii + nrow - 1, column) = matrix%local_data(ifirst:ilast, j)
2796 : ELSE
2797 20865336 : DO i = ifirst, ilast
2798 17083120 : ii = matrix%matrix_struct%row_indices(i) - row_offset(a) + 1 + jj*row_size(a)
2799 20865336 : packed(ii, column) = matrix%local_data(i, j)
2800 : END DO
2801 : END IF
2802 : END DO
2803 : END DO
2804 : END DO
2805 9701143 : IF (.NOT. contributes) CYCLE
2806 : CALL gemm_ctx%gemm('N', 'N', nelement, nimage, nc*(last - first + 1), 1.0_dp, &
2807 : packed, SIZE(packed, 1), phases, SIZE(phases, 1), &
2808 19402286 : 1.0_dp, sums, SIZE(sums, 1))
2809 : END DO
2810 122004461 : IF (groups(slot)%op%identity) THEN
2811 1302791593 : TARGET(:, :) = TARGET + sums
2812 : ELSE
2813 6487055 : left_rot => groups(slot)%op%kind_rot(groups(slot)%op%atom_kind(groups(slot)%inverse(jobs(1, 1))))%rmat
2814 6487055 : right_rot => groups(slot)%op%kind_rot(groups(slot)%op%atom_kind(groups(slot)%inverse(jobs(2, 1))))%rmat
2815 54107001 : DO image = 1, nimage
2816 47619946 : source_block(1:row_size(a), 1:col_size(b)) => sums(:, image)
2817 47619946 : target_block(1:row_size(jobs(1, image)), 1:col_size(jobs(2, image))) => TARGET(:, image)
2818 : CALL kp_symmetry_rotate_block(source_block, left_rot, right_rot, trans, &
2819 63808144 : 1.0_dp, target_block, rotation_work)
2820 : END DO
2821 : END IF
2822 : END DO
2823 : END DO
2824 2903622 : END SUBROUTINE kp_density_contract_tile
2825 :
2826 : ! **************************************************************************************************
2827 : !> \brief Sum compatible group-local FM tiles before returning each image to DBCSR.
2828 : !> The single-rank case stores directly. Incompatible layouts and cheaper
2829 : !> local sparse sums return to the common batched transform in the caller.
2830 : !> \param kpoint ...
2831 : !> \param source borrowed density matrices for all local k-points, components and spins
2832 : !> \param denmat ...
2833 : !> \param components real/imaginary DBCSR work matrices
2834 : !> \param fmwork global FM work matrices, unused on one rank
2835 : !> \param plan neighbor-list traversal and storage conventions
2836 : !> \param completed whether all spin/image results were produced
2837 : ! **************************************************************************************************
2838 38 : SUBROUTINE kpoint_density_transform_regular_grid(kpoint, source, denmat, components, fmwork, plan, completed)
2839 :
2840 : TYPE(kpoint_type), POINTER :: kpoint
2841 : TYPE(cp_fm_p_type), DIMENSION(:, :, :), INTENT(IN) :: source
2842 : TYPE(dbcsr_p_type), DIMENSION(:, :) :: denmat
2843 : TYPE(dbcsr_p_type), DIMENSION(:), INTENT(IN) :: components
2844 : TYPE(cp_fm_type), DIMENSION(:), INTENT(IN), TARGET :: fmwork
2845 : TYPE(kp_transform_plan_type), INTENT(IN) :: plan
2846 : LOGICAL, INTENT(OUT) :: completed
2847 :
2848 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_density_transform_regular_grid'
2849 :
2850 : INTEGER :: compatible, element, first, handle, i, &
2851 : ic, igroup, ik, ikp, image, ispin, j, &
2852 : kplocal, nc, ncol, nrow
2853 : INTEGER, DIMENSION(14) :: layout, layout_max, layout_min
2854 : INTEGER, DIMENSION(2) :: fft_range
2855 : LOGICAL :: found, local, same_layout
2856 : REAL(KIND=dp) :: arg, dense_work, sparse_work
2857 38 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: phase
2858 38 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: fft_density
2859 38 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: block
2860 456 : TYPE(copy_info_type), DIMENSION(2) :: info
2861 : TYPE(cp_fm_struct_type), POINTER :: fms
2862 : TYPE(cp_fm_type) :: dummy
2863 114 : TYPE(cp_fm_type), DIMENSION(2), TARGET :: partial
2864 38 : TYPE(cp_fm_type), DIMENSION(:), POINTER :: image_source
2865 : TYPE(mp_para_env_type), POINTER :: inter, para_env
2866 :
2867 38 : completed = .FALSE.
2868 38 : para_env => kpoint%blacs_env_all%para_env
2869 38 : inter => kpoint%para_env_inter_kp
2870 38 : local = para_env%num_pe == 1
2871 38 : kplocal = SIZE(source, 1)
2872 38 : nc = SIZE(source, 2)
2873 38 : CPASSERT(nc >= 1 .AND. nc <= 2)
2874 38 : fms => source(1, 1, 1)%matrix%matrix_struct
2875 38 : same_layout = .TRUE.
2876 112 : DO ispin = 1, SIZE(source, 3)
2877 260 : DO ic = 1, nc
2878 7262 : DO ikp = 1, kplocal
2879 7040 : same_layout = same_layout .AND. cp_fm_struct_equivalent(fms, source(ikp, ic, ispin)%matrix%matrix_struct)
2880 : same_layout = same_layout .AND. &
2881 21268 : ALL(fms%first_p_pos == source(ikp, ic, ispin)%matrix%matrix_struct%first_p_pos)
2882 : END DO
2883 : END DO
2884 : END DO
2885 38 : CALL cp_fm_get_info(source(1, 1, 1)%matrix, nrow_local=nrow, ncol_local=ncol)
2886 : layout = [fms%nrow_global, fms%ncol_global, fms%nrow_block, fms%ncol_block, &
2887 : fms%first_p_pos, fms%context%num_pe, fms%context%mepos, &
2888 874 : SHAPE(source(1, 1, 1)%matrix%local_data), nrow, ncol]
2889 38 : layout_min = layout
2890 38 : layout_max = layout
2891 38 : IF (inter%num_pe > 1) THEN
2892 36 : CALL inter%min(layout_min)
2893 36 : CALL inter%max(layout_max)
2894 : END IF
2895 570 : compatible = MERGE(1, 0, same_layout .AND. ALL(layout_min == layout_max))
2896 38 : IF (.NOT. local) CALL para_env%min(compatible)
2897 38 : IF (compatible == 0) RETURN
2898 :
2899 : ! Count the actual stored block work, so a sparse local transform is not
2900 : ! replaced by a dense sum merely because the MPI image route is eligible.
2901 38 : sparse_work = -1.0_dp
2902 38 : dense_work = REAL(nrow, dp)*REAL(ncol, dp)*SIZE(denmat, 2)
2903 38 : IF (local) THEN
2904 0 : sparse_work = 0.0_dp
2905 0 : DO igroup = 1, plan%ngroup
2906 0 : first = plan%group_start(igroup)
2907 0 : CALL dbcsr_get_readonly_block_p(components(1)%matrix, plan%row(first), plan%col(first), block, found=found)
2908 0 : IF (found) sparse_work = sparse_work + REAL(SIZE(block), dp)
2909 : END DO
2910 : END IF
2911 :
2912 38 : CALL timeset(routineN, handle)
2913 152 : ALLOCATE (phase(kplocal, nc))
2914 112 : DO ispin = 1, SIZE(source, 3)
2915 : CALL kp_density_fft(kpoint, source(:, :, ispin), nrow, ncol, SIZE(denmat, 2), &
2916 74 : fft_range, fft_density, local)
2917 74 : IF (ispin == 1) THEN
2918 38 : IF (local .AND. .NOT. ALLOCATED(fft_density) .AND. dense_work > sparse_work) EXIT
2919 114 : DO ic = 1, nc
2920 114 : CALL cp_fm_create(partial(ic), fms)
2921 : END DO
2922 38 : image_source => partial
2923 38 : IF (.NOT. local) THEN
2924 38 : CPASSERT(SIZE(fmwork) >= nc)
2925 38 : image_source => fmwork
2926 : END IF
2927 : END IF
2928 2708 : DO image = 1, SIZE(denmat, 2)
2929 7902 : DO ic = 1, nc
2930 335358 : partial(ic)%local_data(:, :) = 0.0_dp
2931 : END DO
2932 2634 : IF (ALLOCATED(fft_density)) THEN
2933 : !$OMP PARALLEL DO DEFAULT(NONE) SCHEDULE(STATIC) &
2934 42 : !$OMP SHARED(partial, fft_density, fft_range, image, nc, nrow) PRIVATE(element, i, j, ic)
2935 : DO element = fft_range(1), fft_range(2)
2936 : i = MODULO(element - 1, nrow) + 1
2937 : j = (element - 1)/nrow + 1
2938 : DO ic = 1, nc
2939 : partial(ic)%local_data(i, j) = fft_density(element - fft_range(1) + 1, ic, image)
2940 : END DO
2941 : END DO
2942 : !$OMP END PARALLEL DO
2943 : ELSE
2944 127008 : DO ikp = 1, kplocal
2945 124416 : ik = kpoint%kp_range(1) + ikp - 1
2946 497664 : arg = twopi*DOT_PRODUCT(REAL(kpoint%index_to_cell(:, image), dp), kpoint%xkp(:, ik))
2947 124416 : phase(ikp, 1) = kpoint%wkp(ik)*COS(arg)
2948 127008 : IF (nc == 2) phase(ikp, 2) = kpoint%wkp(ik)*SIN(arg)
2949 : END DO
2950 : ! Keep real/imaginary sums separate for the neighbor-list signs.
2951 : !$OMP PARALLEL DO DEFAULT(NONE) SCHEDULE(STATIC) &
2952 2592 : !$OMP SHARED(partial, source, phase, nc, nrow, ncol, kplocal, ispin) PRIVATE(j, ikp, ic)
2953 : DO j = 1, ncol
2954 : DO ikp = 1, kplocal
2955 : DO ic = 1, nc
2956 : partial(ic)%local_data(1:nrow, j) = partial(ic)%local_data(1:nrow, j) + &
2957 : phase(ikp, ic)*source(ikp, ic, ispin)%matrix%local_data(1:nrow, j)
2958 : END DO
2959 : END DO
2960 : END DO
2961 : !$OMP END PARALLEL DO
2962 : END IF
2963 :
2964 2634 : IF (.NOT. local) THEN
2965 7902 : DO ic = 1, nc
2966 5268 : IF (inter%num_pe > 1) CALL inter%sum(partial(ic)%local_data)
2967 7902 : IF (kpoint%iogrp) THEN
2968 2676 : CALL cp_fm_start_copy_general(partial(ic), fmwork(ic), para_env, info(ic))
2969 : ELSE
2970 2592 : CALL cp_fm_start_copy_general(dummy, fmwork(ic), para_env, info(ic))
2971 : END IF
2972 : END DO
2973 7902 : DO ic = 1, nc
2974 5268 : CALL cp_fm_finish_copy_general(fmwork(ic), info(ic))
2975 7902 : IF (kpoint%iogrp) CALL cp_fm_cleanup_copy_general(info(ic))
2976 : END DO
2977 : END IF
2978 7902 : DO ic = 1, nc
2979 7902 : CALL kp_copy_fm_to_dbcsr(image_source(ic), components(ic)%matrix, local)
2980 : END DO
2981 : CALL kp_accumulate_density_image(denmat(ispin, image)%matrix, components(1)%matrix, &
2982 2708 : components(2)%matrix, image, nc == 1, plan)
2983 : END DO
2984 74 : IF (ALLOCATED(fft_density)) DEALLOCATE (fft_density)
2985 112 : completed = .TRUE.
2986 : END DO
2987 38 : IF (completed) THEN
2988 114 : DO ic = 1, nc
2989 114 : CALL cp_fm_release(partial(ic))
2990 : END DO
2991 : END IF
2992 38 : CALL timestop(handle)
2993 :
2994 190 : END SUBROUTINE kpoint_density_transform_regular_grid
2995 :
2996 : ! **************************************************************************************************
2997 : !> \brief Exchange k-distributed FM entries into batched FFT pencils.
2998 : !> Each inter-group rank transforms disjoint FM entries, not a full replicated
2999 : !> set of matrices. Real F(R) = A(R)+B(R), real F(-R) = A(R)-B(R), where A and B
3000 : !> are the weighted cosine/real and sine/imaginary sums needed by the DBCSR
3001 : !> storage convention. Original reduced-grid weights are used without doubling.
3002 : !> \param kpoint ...
3003 : !> \param source current group-local real/imaginary density matrices
3004 : !> \param nrow number of valid local FM rows (excluding padding)
3005 : !> \param ncol number of valid local FM columns
3006 : !> \param nimg number of requested real-space images
3007 : !> \param owned local FM element range assigned to this inter-group rank
3008 : !> \param density allocated on success; unallocated selects the regular-grid direct sum
3009 : !> \param local all k-points and matrix entries are on this rank
3010 : ! **************************************************************************************************
3011 74 : SUBROUTINE kp_density_fft(kpoint, source, nrow, ncol, nimg, owned, density, local)
3012 :
3013 : TYPE(kpoint_type), POINTER :: kpoint
3014 : TYPE(cp_fm_p_type), DIMENSION(:, :), INTENT(IN) :: source
3015 : INTEGER, INTENT(IN) :: nrow, ncol, nimg
3016 : INTEGER, DIMENSION(2), INTENT(OUT) :: owned
3017 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :), &
3018 : INTENT(OUT) :: density
3019 : LOGICAL, INTENT(IN) :: local
3020 :
3021 : INTEGER(KIND=int_8), PARAMETER :: max_bytes = 64_int_8*1024*1024, &
3022 : min_fft_peer_bytes = 32_int_8*1024
3023 : INTEGER, PARAMETER :: max_batch = 32
3024 :
3025 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:), &
3026 74 : TARGET :: recvbuf
3027 74 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: values_rs
3028 : COMPLEX(KIND=dp), CONTIGUOUS, DIMENSION(:), &
3029 74 : POINTER :: sendbuf
3030 : COMPLEX(KIND=dp), CONTIGUOUS, DIMENSION(:, :, :), &
3031 74 : POINTER :: values_k
3032 : INTEGER :: batch, count, element, first, group, handle, i, ikp, j, last, local_k, &
3033 : min_local_k, mine, nc, nel, ngroup, nkp, nowned, offset, pos, width
3034 : INTEGER(KIND=int_8) :: fixed_bytes, peer_bytes, per_entry, &
3035 : tile_size
3036 74 : INTEGER, ALLOCATABLE, DIMENSION(:) :: rcount, rdispl, scount, sdispl
3037 74 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: cells, ranges
3038 : INTEGER, DIMENSION(2) :: choice
3039 : INTEGER, DIMENSION(3) :: nfft
3040 : LOGICAL :: compatible
3041 296 : TYPE(k_grid_to_cell_work_type) :: fft_work_data
3042 : TYPE(mp_para_env_type), POINTER :: inter
3043 :
3044 74 : owned = [1, 0]
3045 74 : IF (kpoint%lattice_fft == lattice_fft_off) RETURN
3046 74 : nkp = SIZE(kpoint%xkp, 2)
3047 74 : IF (kpoint%lattice_fft == lattice_fft_auto .AND. nkp < 27) RETURN
3048 : ! Preserve AUTO's local direct sum until a batched FFT executor can amortize
3049 : ! per-transform threading. ON uses FFT on any eligible rank/thread layout.
3050 74 : IF (kpoint%lattice_fft == lattice_fft_auto .AND. local) RETURN
3051 296 : IF (ANY(kpoint%nkp_grid <= 0)) RETURN
3052 : ! Bound map/scratch allocations before attempting a candidate-grid mapping.
3053 296 : IF (32_int_8*PRODUCT(INT(kpoint%nkp_grid, int_8)) > max_bytes) RETURN
3054 74 : compatible = lattice_fft_shape(kpoint%nkp_grid, nfft, allow_arbitrary=.TRUE.)
3055 74 : IF (.NOT. compatible) RETURN
3056 296 : IF (32_int_8*PRODUCT(INT(nfft, int_8)) > max_bytes) RETURN
3057 74 : compatible = regular_kpoint_grid(kpoint%xkp, kpoint%nkp_grid, allow_incomplete=.TRUE.)
3058 74 : IF (.NOT. compatible) RETURN
3059 :
3060 74 : inter => kpoint%para_env_inter_kp
3061 74 : ngroup = inter%num_pe
3062 74 : mine = inter%mepos + 1
3063 74 : local_k = SIZE(source, 1)
3064 74 : nc = SIZE(source, 2)
3065 74 : tile_size = INT(nrow, int_8)*ncol
3066 : ! Avoid default-integer overflow, including float-rounded partition limits.
3067 74 : nel = 0
3068 74 : IF (tile_size <= HUGE(nel)) nel = INT(tile_size)
3069 222 : ALLOCATE (ranges(2, ngroup))
3070 220 : DO group = 1, ngroup
3071 146 : ranges(1, group) = INT(INT(nel, int_8)*(group - 1)/ngroup) + 1
3072 220 : ranges(2, group) = INT(INT(nel, int_8)*group/ngroup)
3073 : END DO
3074 222 : owned = ranges(:, mine)
3075 74 : nowned = MAX(0, owned(2) - owned(1) + 1)
3076 : fixed_bytes = 8_int_8*nowned*nc*nimg + 32_int_8*PRODUCT(INT(nfft, int_8)) + &
3077 : 4_int_8*PRODUCT(INT(kpoint%nkp_grid, int_8)) + 44_int_8*nkp + 52_int_8*nc*nimg + &
3078 518 : 24_int_8*ngroup + 64_int_8
3079 74 : per_entry = 16_int_8*(INT(ngroup, int_8)*local_k + nkp + INT(nc, int_8)*nimg)
3080 74 : width = INT(MAX(0_int_8, MIN(INT(max_batch, int_8), (max_bytes - fixed_bytes)/per_entry)))
3081 222 : choice = [MERGE(1, 0, tile_size <= HUGE(nel)), width]
3082 : ! All global ranks must either use FFT pencils or retain the direct path.
3083 74 : IF (kpoint%blacs_env_all%para_env%num_pe > 1) CALL kpoint%blacs_env_all%para_env%min(choice)
3084 74 : IF (choice(1) == 0 .OR. choice(2) == 0) RETURN
3085 74 : width = choice(2)
3086 74 : IF (kpoint%lattice_fft == lattice_fft_auto .AND. ngroup > 1) THEN
3087 : ! With several k-point groups, small per-peer payloads do not amortize
3088 : ! the all-to-all latency and the regular-grid direct sum is faster.
3089 216 : min_local_k = MINVAL(kpoint%kp_dist(2, :) - kpoint%kp_dist(1, :) + 1)
3090 72 : peer_bytes = 16_int_8*INT(width, int_8)*INT(min_local_k, int_8)
3091 72 : IF (peer_bytes < min_fft_peer_bytes) RETURN
3092 : END IF
3093 :
3094 2 : CALL timeset("kp_density_fft", handle)
3095 10 : ALLOCATE (density(nowned, nc, nimg))
3096 6 : ALLOCATE (cells(3, nc*nimg))
3097 170 : cells(:, 1:nimg) = kpoint%index_to_cell(:, 1:nimg)
3098 170 : IF (nc == 2) cells(:, nimg + 1:) = -cells(:, 1:nimg)
3099 12 : ALLOCATE (scount(ngroup), sdispl(ngroup), rcount(ngroup), rdispl(ngroup))
3100 6 : ALLOCATE (recvbuf(MAX(1, width*nkp)))
3101 2 : IF (ngroup == 1) THEN
3102 2 : sendbuf => recvbuf
3103 : ELSE
3104 0 : ALLOCATE (sendbuf(MAX(1, width*ngroup*local_k)))
3105 : END IF
3106 8 : ALLOCATE (values_rs(width, 1, nc*nimg))
3107 2 : IF (nowned > 0) THEN
3108 : CALL k_grid_to_cell_prepare(fft_work_data, kpoint%xkp, kpoint%nkp_grid, cells, &
3109 2 : weights=kpoint%wkp, allow_incomplete=.TRUE.)
3110 : END IF
3111 166 : DO batch = 1, MAXVAL(ranges(2, :) - ranges(1, :) + 1), width
3112 162 : count = MAX(0, MIN(width, nowned - batch + 1))
3113 162 : offset = 0
3114 324 : DO group = 1, ngroup
3115 162 : first = ranges(1, group) + batch - 1
3116 162 : last = MIN(first + width - 1, ranges(2, group))
3117 162 : scount(group) = MAX(0, last - first + 1)*local_k
3118 162 : sdispl(group) = offset
3119 162 : offset = offset + scount(group)
3120 162 : rcount(group) = count*(kpoint%kp_dist(2, group) - kpoint%kp_dist(1, group) + 1)
3121 162 : rdispl(group) = count*(kpoint%kp_dist(1, group) - 1)
3122 : ! Pack elements in k-major order; displacements already encode the
3123 : ! unequal k ranges, so neither counts nor indices need an exchange.
3124 : !$OMP PARALLEL DO DEFAULT(NONE) SCHEDULE(STATIC) &
3125 : !$OMP SHARED(source, sendbuf, sdispl, group, first, last, nc, nrow, local_k) &
3126 324 : !$OMP PRIVATE(ikp, element, i, j, pos)
3127 : DO ikp = 1, local_k
3128 : DO element = first, last
3129 : i = MODULO(element - 1, nrow) + 1
3130 : j = (element - 1)/nrow + 1
3131 : pos = sdispl(group) + (ikp - 1)*(last - first + 1) + element - first + 1
3132 : sendbuf(pos) = CMPLX(source(ikp, 1)%matrix%local_data(i, j), 0.0_dp, dp)
3133 : IF (nc == 2) sendbuf(pos) = CMPLX(REAL(sendbuf(pos), dp), &
3134 : source(ikp, 2)%matrix%local_data(i, j), dp)
3135 : END DO
3136 : END DO
3137 : !$OMP END PARALLEL DO
3138 : END DO
3139 162 : IF (ngroup > 1) CALL inter%alltoall(sendbuf, scount, sdispl, recvbuf, rcount, rdispl)
3140 162 : IF (count == 0) CYCLE
3141 162 : values_k(1:count, 1:1, 1:nkp) => recvbuf(1:count*nkp)
3142 : ! Keep fft_tools pool/planner access outside OpenMP worker regions.
3143 162 : CALL k_grid_to_cell_execute(fft_work_data, values_k, values_rs(1:count, :, :))
3144 3566 : DO i = 1, nimg
3145 3564 : IF (nc == 1) THEN
3146 0 : density(batch:batch + count - 1, 1, i) = REAL(values_rs(1:count, 1, i), dp)
3147 : ELSE
3148 : density(batch:batch + count - 1, 1, i) = 0.5_dp* &
3149 112266 : REAL(values_rs(1:count, 1, i) + values_rs(1:count, 1, nimg + i), dp)
3150 : density(batch:batch + count - 1, 2, i) = 0.5_dp* &
3151 112266 : REAL(values_rs(1:count, 1, i) - values_rs(1:count, 1, nimg + i), dp)
3152 : END IF
3153 : END DO
3154 : END DO
3155 2 : CALL k_grid_to_cell_release(fft_work_data)
3156 2 : IF (ngroup > 1) DEALLOCATE (sendbuf)
3157 2 : NULLIFY (sendbuf)
3158 2 : CALL timestop(handle)
3159 :
3160 150 : END SUBROUTINE kp_density_fft
3161 :
3162 : ! **************************************************************************************************
3163 : !> \brief Apply the neighbor-list storage convention to an already k-summed image.
3164 : !> Cell phases and k weights have been applied in the FM distribution;
3165 : !> retain the multiplicity and imaginary sign of each neighbor-list entry.
3166 : !> \param denmat ...
3167 : !> \param rpmat ...
3168 : !> \param cpmat ...
3169 : !> \param image ...
3170 : !> \param real_only ...
3171 : !> \param plan ...
3172 : ! **************************************************************************************************
3173 2634 : SUBROUTINE kp_accumulate_density_image(denmat, rpmat, cpmat, image, real_only, plan)
3174 :
3175 : TYPE(dbcsr_type), POINTER :: denmat, rpmat, cpmat
3176 : INTEGER, INTENT(IN) :: image
3177 : LOGICAL, INTENT(IN) :: real_only
3178 : TYPE(kp_transform_plan_type), INTENT(IN) :: plan
3179 :
3180 : INTEGER :: first, igroup, last
3181 : LOGICAL :: found
3182 2634 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: cblock, dblock, rblock
3183 :
3184 2634 : CALL dbcsr_set(denmat, 0.0_dp)
3185 : !$OMP PARALLEL DO DEFAULT(NONE) SCHEDULE(STATIC) &
3186 : !$OMP SHARED(denmat, rpmat, cpmat, image, real_only, plan) &
3187 2634 : !$OMP PRIVATE(igroup, first, last, found, dblock, rblock, cblock)
3188 : DO igroup = 1, plan%ngroup
3189 : first = plan%group_start(igroup)
3190 : IF (plan%image(first) /= image) CYCLE
3191 : last = plan%group_start(igroup + 1) - 1
3192 : CALL dbcsr_get_block_p(denmat, plan%row(first), plan%col(first), dblock, found=found)
3193 : IF (.NOT. found) CYCLE
3194 : CALL dbcsr_get_readonly_block_p(rpmat, plan%row(first), plan%col(first), rblock, found=found)
3195 : IF (.NOT. found) CYCLE
3196 : IF (.NOT. real_only) THEN
3197 : CALL dbcsr_get_readonly_block_p(cpmat, plan%row(first), plan%col(first), cblock, found=found)
3198 : IF (.NOT. found) CYCLE
3199 : END IF
3200 : dblock = REAL(last - first + 1, dp)*rblock
3201 : IF (.NOT. real_only) dblock = dblock + SUM(plan%symmetry_sign(first:last))*cblock
3202 : END DO
3203 : !$OMP END PARALLEL DO
3204 :
3205 2634 : END SUBROUTINE kp_accumulate_density_image
3206 :
3207 : ! **************************************************************************************************
3208 : !> \brief Copy density FM data to existing DBCSR blocks, with no redistribution on one rank.
3209 : !> \param fm ...
3210 : !> \param matrix ...
3211 : !> \param local all matrix data and DBCSR blocks belong to this rank
3212 : ! **************************************************************************************************
3213 5268 : SUBROUTINE kp_copy_fm_to_dbcsr(fm, matrix, local)
3214 :
3215 : TYPE(cp_fm_type), INTENT(IN) :: fm
3216 : TYPE(dbcsr_type), POINTER :: matrix
3217 : LOGICAL, INTENT(IN) :: local
3218 :
3219 : INTEGER :: col_offset, row_offset
3220 : REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
3221 5268 : POINTER :: full
3222 5268 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: block
3223 : TYPE(dbcsr_iterator_type) :: iterator
3224 :
3225 5268 : IF (.NOT. local) THEN
3226 5268 : CALL copy_fm_to_dbcsr(fm, matrix, keep_sparsity=.TRUE.)
3227 5268 : RETURN
3228 : END IF
3229 0 : CALL cp_fm_get_info(fm, local_data=full)
3230 0 : CALL dbcsr_iterator_start(iterator, matrix, shared=.FALSE.)
3231 0 : DO WHILE (dbcsr_iterator_blocks_left(iterator))
3232 : CALL dbcsr_iterator_next_block(iterator, block=block, row_offset=row_offset, &
3233 0 : col_offset=col_offset)
3234 : block(:, :) = full(row_offset:row_offset + SIZE(block, 1) - 1, &
3235 0 : col_offset:col_offset + SIZE(block, 2) - 1)
3236 : END DO
3237 0 : CALL dbcsr_iterator_stop(iterator)
3238 :
3239 5268 : END SUBROUTINE kp_copy_fm_to_dbcsr
3240 :
3241 : ! **************************************************************************************************
3242 : !> \brief Build the immutable neighbor-list traversal shared by R-to-K and K-to-R transforms.
3243 : !> \param plan ...
3244 : !> \param sab_nl ...
3245 : !> \param cell_to_index ...
3246 : !> \param nimg ...
3247 : !> \param block_template ...
3248 : !> \param group_entries ...
3249 : ! **************************************************************************************************
3250 81436 : SUBROUTINE kp_transform_plan_create(plan, sab_nl, cell_to_index, nimg, block_template, group_entries)
3251 :
3252 : TYPE(kp_transform_plan_type), INTENT(OUT) :: plan
3253 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
3254 : POINTER :: sab_nl
3255 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
3256 : INTEGER, INTENT(IN) :: nimg
3257 : TYPE(dbcsr_type), INTENT(IN), OPTIONAL :: block_template
3258 : LOGICAL, INTENT(IN), OPTIONAL :: group_entries
3259 :
3260 : INTEGER :: i, iatom, icell, icol, igroup, irow, &
3261 : jatom, nblock
3262 81436 : INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:) :: key
3263 81436 : INTEGER, ALLOCATABLE, DIMENSION(:) :: order
3264 : INTEGER, DIMENSION(3) :: cell
3265 81436 : INTEGER, DIMENSION(:), POINTER :: col_offsets, row_offsets
3266 : LOGICAL :: do_grouping, do_symmetric, store_offsets
3267 : TYPE(neighbor_list_iterator_p_type), &
3268 81436 : DIMENSION(:), POINTER :: iterator
3269 :
3270 81436 : CALL get_neighbor_list_set_p(neighbor_list_sets=sab_nl, symmetric=do_symmetric)
3271 81436 : plan%symmetric = do_symmetric
3272 81436 : do_grouping = .FALSE.
3273 81436 : IF (PRESENT(group_entries)) do_grouping = group_entries
3274 81436 : store_offsets = PRESENT(block_template)
3275 81436 : IF (store_offsets) THEN
3276 39912 : CALL dbcsr_get_info(block_template, row_blk_offset=row_offsets, col_blk_offset=col_offsets)
3277 : END IF
3278 81436 : CALL neighbor_list_iterator_create(iterator, sab_nl)
3279 18667741 : DO WHILE (neighbor_list_iterate(iterator) == 0)
3280 18586305 : CALL get_iterator_info(iterator, cell=cell)
3281 18586305 : icell = cell_to_index(cell(1), cell(2), cell(3))
3282 18667741 : IF (icell >= 1 .AND. icell <= nimg) plan%nentry = plan%nentry + 1
3283 : END DO
3284 81436 : CALL neighbor_list_iterator_release(iterator)
3285 :
3286 0 : ALLOCATE (plan%row(plan%nentry), plan%col(plan%nentry), plan%image(plan%nentry), &
3287 721099 : plan%cell(3, plan%nentry), plan%symmetry_sign(plan%nentry))
3288 81436 : IF (store_offsets) THEN
3289 117836 : ALLOCATE (plan%row_offset(plan%nentry), plan%col_offset(plan%nentry))
3290 : END IF
3291 :
3292 81436 : i = 0
3293 81436 : CALL neighbor_list_iterator_create(iterator, sab_nl)
3294 18667741 : DO WHILE (neighbor_list_iterate(iterator) == 0)
3295 18586305 : CALL get_iterator_info(iterator, iatom=iatom, jatom=jatom, cell=cell)
3296 18586305 : icell = cell_to_index(cell(1), cell(2), cell(3))
3297 18586305 : IF (icell < 1 .OR. icell > nimg) CYCLE
3298 18586217 : i = i + 1
3299 18586217 : irow = iatom
3300 18586217 : icol = jatom
3301 18586217 : plan%symmetry_sign(i) = 1.0_dp
3302 18586217 : IF (do_symmetric .AND. iatom > jatom) THEN
3303 7750585 : irow = jatom
3304 7750585 : icol = iatom
3305 7750585 : plan%symmetry_sign(i) = -1.0_dp
3306 : END IF
3307 18586217 : plan%row(i) = irow
3308 18586217 : plan%col(i) = icol
3309 18586217 : IF (store_offsets) THEN
3310 8999738 : plan%row_offset(i) = row_offsets(irow)
3311 8999738 : plan%col_offset(i) = col_offsets(icol)
3312 : END IF
3313 18586217 : plan%image(i) = icell
3314 74426392 : plan%cell(:, i) = cell
3315 : END DO
3316 81436 : CALL neighbor_list_iterator_release(iterator)
3317 81436 : CPASSERT(i == plan%nentry)
3318 :
3319 81436 : IF (do_grouping .AND. plan%nentry > 0) THEN
3320 19213067 : nblock = MAX(MAXVAL(plan%row), MAXVAL(plan%col))
3321 200545 : ALLOCATE (key(plan%nentry), order(plan%nentry))
3322 9626588 : DO i = 1, plan%nentry
3323 : key(i) = INT(plan%image(i), KIND=int_8) + INT(nimg, KIND=int_8)* &
3324 : (INT(plan%row(i) - 1, KIND=int_8) + INT(nblock, KIND=int_8)* &
3325 9626588 : INT(plan%col(i) - 1, KIND=int_8))
3326 : END DO
3327 40109 : CALL sort(key, plan%nentry, order)
3328 19253176 : plan%row(:) = plan%row(order)
3329 19253176 : plan%col(:) = plan%col(order)
3330 40109 : IF (store_offsets) THEN
3331 0 : plan%row_offset(:) = plan%row_offset(order)
3332 0 : plan%col_offset(:) = plan%col_offset(order)
3333 : END IF
3334 19253176 : plan%image(:) = plan%image(order)
3335 76772050 : plan%cell(:, :) = plan%cell(:, order)
3336 19253176 : plan%symmetry_sign(:) = plan%symmetry_sign(order)
3337 :
3338 40109 : plan%ngroup = 1
3339 9586479 : DO i = 2, plan%nentry
3340 9586479 : IF (key(i) /= key(i - 1)) plan%ngroup = plan%ngroup + 1
3341 : END DO
3342 120327 : ALLOCATE (plan%group_start(plan%ngroup + 1))
3343 40109 : igroup = 1
3344 40109 : plan%group_start(igroup) = 1
3345 9586479 : DO i = 2, plan%nentry
3346 9586479 : IF (key(i) /= key(i - 1)) THEN
3347 9546370 : igroup = igroup + 1
3348 9546370 : plan%group_start(igroup) = i
3349 : END IF
3350 : END DO
3351 40109 : plan%group_start(plan%ngroup + 1) = plan%nentry + 1
3352 40109 : DEALLOCATE (key, order)
3353 : END IF
3354 :
3355 162872 : END SUBROUTINE kp_transform_plan_create
3356 :
3357 : ! **************************************************************************************************
3358 : !> \brief Allocate a dense work matrix with the requested shape
3359 : !> \param work dense work matrix
3360 : !> \param nrow number of rows
3361 : !> \param ncol number of columns
3362 : ! **************************************************************************************************
3363 77297863 : SUBROUTINE ensure_work_matrix(work, nrow, ncol)
3364 :
3365 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), &
3366 : INTENT(INOUT) :: work
3367 : INTEGER, INTENT(IN) :: nrow, ncol
3368 :
3369 77297863 : IF (ALLOCATED(work)) THEN
3370 76806427 : IF (SIZE(work, 1) == nrow .AND. SIZE(work, 2) == ncol) RETURN
3371 6768944 : DEALLOCATE (work)
3372 : END IF
3373 29041520 : ALLOCATE (work(nrow, ncol))
3374 :
3375 : END SUBROUTINE ensure_work_matrix
3376 :
3377 : ! **************************************************************************************************
3378 : !> \brief Select the Bloch-phase convention that preserves overlap covariance.
3379 : !> \param kpoint ...
3380 : !> \param overlap_rs ...
3381 : !> \param tempmat ...
3382 : !> \param sab_nl ...
3383 : !> \param cell_to_index ...
3384 : ! **************************************************************************************************
3385 40498 : SUBROUTINE calibrate_symmetry_phases(kpoint, overlap_rs, tempmat, sab_nl, cell_to_index)
3386 :
3387 : TYPE(kpoint_type), POINTER :: kpoint
3388 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: overlap_rs
3389 : TYPE(dbcsr_type), POINTER :: tempmat
3390 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
3391 : POINTER :: sab_nl
3392 : INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
3393 :
3394 : CHARACTER(LEN=*), PARAMETER :: routineN = 'calibrate_symmetry_phases'
3395 :
3396 : CHARACTER(LEN=256) :: phase_error
3397 : INTEGER :: best_mode, handle, iatom, ik, is, key, &
3398 : mode, nrot, rot_slot
3399 40498 : INTEGER, ALLOCATABLE :: best_ik(:), best_is(:), mode_by_op(:)
3400 : LOGICAL :: needs_calibration, reverse
3401 : REAL(KIND=dp) :: arg, best_residual, candidate_norm, &
3402 : direct_norm, overlap_dot, phase_score, &
3403 : phase_tolerance, relative_residual
3404 40498 : REAL(KIND=dp), ALLOCATABLE :: best_score(:)
3405 : TYPE(dbcsr_type), POINTER :: direct_c, direct_r, source_c, source_r, &
3406 : sym_c, sym_r
3407 : TYPE(kp_symmetry_op_type) :: op
3408 : TYPE(kpoint_sym_type), POINTER :: kpsym
3409 :
3410 40498 : needs_calibration = .FALSE.
3411 162920 : DO ik = 1, kpoint%nkp
3412 123760 : kpsym => kpoint%kp_sym(ik)%kpoint_sym
3413 162920 : IF (kpsym%apply_symmetry) THEN
3414 : ! Only the first nwght operations enter the density transform.
3415 151270 : IF (ANY(kpsym%phase_mode(1:kpsym%nwght) == 0)) THEN
3416 : needs_calibration = .TRUE.
3417 : EXIT
3418 : END IF
3419 : END IF
3420 : END DO
3421 40498 : IF (.NOT. needs_calibration) RETURN
3422 :
3423 1338 : CALL timeset(routineN, handle)
3424 1338 : CPASSERT(ASSOCIATED(kpoint%ibrot))
3425 1338 : nrot = SIZE(kpoint%ibrot)
3426 9366 : ALLOCATE (best_ik(2*nrot), best_is(2*nrot), mode_by_op(2*nrot), best_score(2*nrot))
3427 1338 : best_ik(:) = 0
3428 1338 : best_is(:) = 0
3429 1338 : mode_by_op(:) = 0
3430 31478 : best_score(:) = -1.0_dp
3431 :
3432 : ! The Bloch-phase direction is a convention of the signed crystallographic
3433 : ! operation, not of an individual member of its k-point star. Select one
3434 : ! numerically discriminating representative per signed operation and reuse
3435 : ! the result for all equivalent contributions.
3436 4354 : DO ik = 1, kpoint%nkp
3437 3016 : kpsym => kpoint%kp_sym(ik)%kpoint_sym
3438 3016 : IF (.NOT. kpsym%apply_symmetry) CYCLE
3439 16600 : DO is = 1, kpsym%nwght
3440 12520 : rot_slot = find_kpoint_rotation_slot(kpoint, kpsym%rotp(is))
3441 12520 : CPASSERT(rot_slot > 0)
3442 12520 : key = rot_slot + nrot*MERGE(1, 0, kpsym%rotp(is) < 0)
3443 12520 : IF (kpsym%phase_mode(is) > 0) THEN
3444 0 : IF (mode_by_op(key) == 0) THEN
3445 0 : mode_by_op(key) = kpsym%phase_mode(is)
3446 : ELSE
3447 0 : CPASSERT(mode_by_op(key) == kpsym%phase_mode(is))
3448 : END IF
3449 : CYCLE
3450 : END IF
3451 :
3452 12520 : phase_score = 0.0_dp
3453 77096 : DO iatom = 2, SIZE(kpsym%fcell_gauge, 2)
3454 : arg = DOT_PRODUCT(REAL(kpsym%fcell_gauge(:, iatom, is) - &
3455 : kpsym%fcell_gauge(:, 1, is), KIND=dp), &
3456 258304 : kpsym%xkp(:, is))
3457 77096 : phase_score = MAX(phase_score, ABS(SIN(twopi*arg)))
3458 : END DO
3459 15536 : IF (phase_score > best_score(key)) THEN
3460 6650 : best_score(key) = phase_score
3461 6650 : best_ik(key) = ik
3462 6650 : best_is(key) = is
3463 : END IF
3464 : END DO
3465 : END DO
3466 :
3467 1338 : ALLOCATE (source_r, source_c, direct_r, direct_c, sym_r, sym_c)
3468 1338 : CALL dbcsr_create(source_r, template=tempmat, matrix_type=dbcsr_type_symmetric)
3469 1338 : CALL dbcsr_create(source_c, template=tempmat, matrix_type=dbcsr_type_antisymmetric)
3470 1338 : CALL dbcsr_create(direct_r, template=tempmat, matrix_type=dbcsr_type_symmetric)
3471 1338 : CALL dbcsr_create(direct_c, template=tempmat, matrix_type=dbcsr_type_antisymmetric)
3472 1338 : CALL dbcsr_create(sym_r, template=tempmat, matrix_type=dbcsr_type_symmetric)
3473 1338 : CALL dbcsr_create(sym_c, template=tempmat, matrix_type=dbcsr_type_antisymmetric)
3474 1338 : CALL cp_dbcsr_alloc_block_from_nbl(source_r, sab_nl)
3475 1338 : CALL cp_dbcsr_alloc_block_from_nbl(source_c, sab_nl)
3476 1338 : CALL cp_dbcsr_alloc_block_from_nbl(direct_r, sab_nl)
3477 1338 : CALL cp_dbcsr_alloc_block_from_nbl(direct_c, sab_nl)
3478 1338 : CALL cp_dbcsr_alloc_block_from_nbl(sym_r, sab_nl)
3479 1338 : CALL cp_dbcsr_alloc_block_from_nbl(sym_c, sab_nl)
3480 :
3481 1338 : phase_tolerance = MAX(1.0e-6_dp, 100.0_dp*kpoint%eps_geo)
3482 31478 : DO key = 1, 2*nrot
3483 30140 : IF (mode_by_op(key) > 0 .OR. best_ik(key) == 0) CYCLE
3484 : ! If both phase directions are algebraically indistinguishable for every
3485 : ! occurrence of this operation, avoid an unnecessary overlap transform.
3486 6480 : IF (best_score(key) <= 1000.0_dp*EPSILON(1.0_dp)) THEN
3487 4392 : mode_by_op(key) = 1
3488 4392 : CYCLE
3489 : END IF
3490 :
3491 2088 : ik = best_ik(key)
3492 2088 : is = best_is(key)
3493 2088 : kpsym => kpoint%kp_sym(ik)%kpoint_sym
3494 2088 : CALL dbcsr_set(source_r, 0.0_dp)
3495 2088 : CALL dbcsr_set(source_c, 0.0_dp)
3496 : CALL rskp_transform(source_r, source_c, overlap_rs, 1, kpoint%xkp(1:3, ik), &
3497 2088 : cell_to_index, sab_nl)
3498 2088 : CALL dbcsr_set(direct_r, 0.0_dp)
3499 2088 : CALL dbcsr_set(direct_c, 0.0_dp)
3500 : CALL rskp_transform(direct_r, direct_c, overlap_rs, 1, kpsym%xkp(1:3, is), &
3501 2088 : cell_to_index, sab_nl)
3502 2088 : CALL dbcsr_dot(direct_r, direct_r, direct_norm)
3503 2088 : CALL dbcsr_dot(direct_c, direct_c, candidate_norm)
3504 2088 : direct_norm = direct_norm + candidate_norm
3505 :
3506 2088 : CALL kp_symmetry_op_init(op, kpoint, kpsym, is)
3507 2088 : best_mode = 0
3508 2088 : best_residual = HUGE(1.0_dp)
3509 6264 : DO mode = 1, 2
3510 4176 : reverse = mode == 2
3511 4176 : CALL symtrans_phase(sym_r, sym_c, source_r, source_c, .FALSE., op, reverse)
3512 4176 : CALL dbcsr_dot(sym_r, sym_r, candidate_norm)
3513 4176 : CALL dbcsr_dot(sym_c, sym_c, relative_residual)
3514 4176 : candidate_norm = candidate_norm + relative_residual
3515 4176 : CALL dbcsr_dot(sym_r, direct_r, overlap_dot)
3516 4176 : CALL dbcsr_dot(sym_c, direct_c, relative_residual)
3517 4176 : overlap_dot = overlap_dot + relative_residual
3518 : relative_residual = SQRT(MAX(0.0_dp, candidate_norm + direct_norm - &
3519 4176 : 2.0_dp*overlap_dot)/MAX(direct_norm, TINY(1.0_dp)))
3520 6264 : IF (relative_residual < best_residual) THEN
3521 4176 : best_residual = relative_residual
3522 4176 : best_mode = mode
3523 : END IF
3524 : END DO
3525 2088 : IF (best_residual > phase_tolerance) THEN
3526 : WRITE (phase_error, '(A,ES12.4,A,I0,A,I0)') &
3527 0 : "No Bloch-phase direction preserves overlap covariance; residual=", &
3528 0 : best_residual, ", irreducible k-point=", ik, ", operation=", is
3529 0 : CALL cp_abort(__LOCATION__, TRIM(phase_error))
3530 : END IF
3531 29174 : mode_by_op(key) = best_mode
3532 : END DO
3533 :
3534 4354 : DO ik = 1, kpoint%nkp
3535 3016 : kpsym => kpoint%kp_sym(ik)%kpoint_sym
3536 3016 : IF (.NOT. kpsym%apply_symmetry) CYCLE
3537 16600 : DO is = 1, kpsym%nwght
3538 12520 : IF (kpsym%phase_mode(is) > 0) CYCLE
3539 12520 : rot_slot = find_kpoint_rotation_slot(kpoint, kpsym%rotp(is))
3540 12520 : CPASSERT(rot_slot > 0)
3541 12520 : key = rot_slot + nrot*MERGE(1, 0, kpsym%rotp(is) < 0)
3542 12520 : CPASSERT(mode_by_op(key) > 0)
3543 15536 : kpsym%phase_mode(is) = mode_by_op(key)
3544 : END DO
3545 : END DO
3546 :
3547 1338 : CALL dbcsr_deallocate_matrix(source_r)
3548 1338 : CALL dbcsr_deallocate_matrix(source_c)
3549 1338 : CALL dbcsr_deallocate_matrix(direct_r)
3550 1338 : CALL dbcsr_deallocate_matrix(direct_c)
3551 1338 : CALL dbcsr_deallocate_matrix(sym_r)
3552 1338 : CALL dbcsr_deallocate_matrix(sym_c)
3553 1338 : CALL timestop(handle)
3554 :
3555 203828 : END SUBROUTINE calibrate_symmetry_phases
3556 :
3557 : ! **************************************************************************************************
3558 : !> \brief Borrow the atom mapping and basis rotations for one symmetry contribution.
3559 : !> \param op non-owning operation view
3560 : !> \param kpoint k-point environment
3561 : !> \param kpsym symmetry data for the representative k-point
3562 : !> \param is index in the existing symmetry contribution list
3563 : ! **************************************************************************************************
3564 313688 : SUBROUTINE kp_symmetry_op_init(op, kpoint, kpsym, is)
3565 : TYPE(kp_symmetry_op_type), INTENT(OUT) :: op
3566 : TYPE(kpoint_type), POINTER :: kpoint
3567 : TYPE(kpoint_sym_type), POINTER :: kpsym
3568 : INTEGER, INTENT(IN) :: is
3569 :
3570 : INTEGER :: iatom, rot_slot
3571 : LOGICAL :: has_phase, perm, rotates
3572 : REAL(KIND=dp) :: dr
3573 78422 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: rot
3574 :
3575 78422 : rot_slot = find_kpoint_rotation_slot(kpoint, kpsym%rotp(is))
3576 78422 : CPASSERT(rot_slot > 0)
3577 78422 : op%kind_rot => kpoint%kind_rotmat(rot_slot, :)
3578 78422 : op%atom_map => kpsym%f0(:, is)
3579 78422 : op%atom_kind => kpoint%atype
3580 78422 : op%cell_shift => kpsym%fcell_gauge(:, :, is)
3581 313688 : op%xkp(:) = kpsym%xkp(:, is)
3582 78422 : op%time_reversal = kpsym%rotp(is) < 0
3583 :
3584 78422 : perm = .FALSE.
3585 279620 : DO iatom = 1, SIZE(op%atom_map)
3586 229640 : IF (op%atom_map(iatom) == iatom) CYCLE
3587 : perm = .TRUE.
3588 251178 : EXIT
3589 : END DO
3590 78422 : rot => kpsym%rot(:, :, is)
3591 78422 : dr = ABS(rot(1, 1) - 1.0_dp) + ABS(rot(2, 2) - 1.0_dp) + ABS(rot(3, 3) - 1.0_dp)
3592 1051762 : rotates = ABS(SUM(ABS(rot)) - 3.0_dp) > 1.e-12_dp .OR. ABS(dr) > 1.e-12_dp
3593 949956 : has_phase = ANY(op%cell_shift /= 0) .OR. op%time_reversal
3594 78422 : op%identity = .NOT. (rotates .OR. perm .OR. has_phase)
3595 78422 : op%redistribute = perm .OR. has_phase
3596 :
3597 78422 : END SUBROUTINE kp_symmetry_op_init
3598 :
3599 : ! **************************************************************************************************
3600 : !> \brief DBCSR adapter for a complex k-point symmetry operation.
3601 : !> \param srpmat real part of transformed matrix
3602 : !> \param scpmat imaginary part of transformed matrix
3603 : !> \param rpmat real part of reference matrix
3604 : !> \param cpmat imaginary part of reference matrix
3605 : !> \param real_only whether only the real part is present
3606 : !> \param op symmetry operation and atom mapping
3607 : !> \param reverse_phase use the reverse Bloch-phase convention
3608 : ! **************************************************************************************************
3609 4176 : SUBROUTINE symtrans_phase(srpmat, scpmat, rpmat, cpmat, real_only, op, reverse_phase)
3610 : TYPE(dbcsr_type), POINTER :: srpmat, scpmat, rpmat, cpmat
3611 : LOGICAL, INTENT(IN) :: real_only
3612 : TYPE(kp_symmetry_op_type), INTENT(IN) :: op
3613 : LOGICAL, INTENT(IN) :: reverse_phase
3614 :
3615 : CHARACTER(LEN=*), PARAMETER :: routineN = 'symtrans_phase'
3616 :
3617 : INTEGER :: handle, icol, ip, irow, jcol, jp, jrow, &
3618 : mynode, nthreads, numnodes, owner
3619 : INTEGER, DIMENSION(3) :: shift
3620 : LOGICAL :: found, trans
3621 4176 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: cwork, rwork, twork
3622 4176 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: cblock, left_rot, rblock, right_rot, &
3623 4176 : scblock, srblock
3624 : TYPE(dbcsr_distribution_type) :: dist
3625 : TYPE(dbcsr_iterator_type) :: iter
3626 :
3627 4176 : CALL timeset(routineN, handle)
3628 :
3629 4176 : IF (op%identity) THEN
3630 0 : CALL dbcsr_copy(srpmat, rpmat)
3631 0 : IF (.NOT. real_only) CALL dbcsr_copy(scpmat, cpmat)
3632 0 : CALL timestop(handle)
3633 0 : RETURN
3634 : END IF
3635 :
3636 4176 : CALL dbcsr_get_info(rpmat, distribution=dist)
3637 4176 : CALL dbcsr_distribution_get(dist, mynode=mynode, numnodes=numnodes)
3638 4176 : IF (numnodes /= 1 .AND. op%redistribute) THEN
3639 4176 : CALL dbcsr_replicate_all(rpmat)
3640 4176 : IF (.NOT. real_only) CALL dbcsr_replicate_all(cpmat)
3641 : END IF
3642 :
3643 4176 : CALL dbcsr_set(srpmat, 0.0_dp)
3644 4176 : IF (.NOT. real_only) CALL dbcsr_set(scpmat, 0.0_dp)
3645 :
3646 4176 : nthreads = 1
3647 4176 : !$ nthreads = omp_get_max_threads()
3648 : ! Atom permutations preserve row ownership, while block scheduling can race on a target row.
3649 : !$OMP PARALLEL DEFAULT(NONE) NUM_THREADS(nthreads) &
3650 : !$OMP SHARED(rpmat,cpmat,srpmat,scpmat,real_only,op,reverse_phase,mynode) &
3651 : !$OMP PRIVATE(iter,irow,icol,rblock,cblock,rwork,cwork,twork,left_rot,right_rot,shift, &
3652 : !$OMP found,ip,jp,jrow,jcol,trans,srblock,scblock,owner)
3653 : CALL dbcsr_iterator_readonly_start(iter, rpmat, dynamic=.TRUE., dynamic_byrows=.TRUE.)
3654 : DO WHILE (dbcsr_iterator_blocks_left(iter))
3655 : CALL dbcsr_iterator_next_block(iter, irow, icol, rblock)
3656 : shift(:) = op%cell_shift(:, icol) - op%cell_shift(:, irow)
3657 : IF (reverse_phase) shift(:) = -shift
3658 : IF (real_only) THEN
3659 : CALL kp_symmetry_phase_block(rblock, shift, op%xkp, op%time_reversal, rwork)
3660 : ELSE
3661 : CALL dbcsr_get_readonly_block_p(matrix=cpmat, row=irow, col=icol, block=cblock, found=found)
3662 : IF (.NOT. found) NULLIFY (cblock)
3663 : CALL kp_symmetry_phase_block(rblock, shift, op%xkp, op%time_reversal, rwork, cwork, cblock)
3664 : END IF
3665 :
3666 : ip = op%atom_map(irow)
3667 : jp = op%atom_map(icol)
3668 : jrow = MIN(ip, jp)
3669 : jcol = MAX(ip, jp)
3670 : trans = ip > jp
3671 : IF (trans) THEN
3672 : left_rot => op%kind_rot(op%atom_kind(icol))%rmat
3673 : right_rot => op%kind_rot(op%atom_kind(irow))%rmat
3674 : ELSE
3675 : left_rot => op%kind_rot(op%atom_kind(irow))%rmat
3676 : right_rot => op%kind_rot(op%atom_kind(icol))%rmat
3677 : END IF
3678 :
3679 : CALL dbcsr_get_block_p(matrix=srpmat, row=jrow, col=jcol, block=srblock, found=found)
3680 : IF (.NOT. found) THEN
3681 : CALL dbcsr_get_stored_coordinates(srpmat, jrow, jcol, owner)
3682 : CPASSERT(owner /= mynode)
3683 : CYCLE
3684 : END IF
3685 : CALL kp_symmetry_rotate_block(rwork, left_rot, right_rot, trans, 1.0_dp, srblock, twork)
3686 : IF (.NOT. real_only) THEN
3687 : CALL dbcsr_get_block_p(matrix=scpmat, row=jrow, col=jcol, block=scblock, found=found)
3688 : CPASSERT(found)
3689 : ! Returning to the stored upper triangle conjugates the imaginary part.
3690 : CALL kp_symmetry_rotate_block(cwork, left_rot, right_rot, trans, &
3691 : MERGE(-1.0_dp, 1.0_dp, trans), scblock, twork)
3692 : END IF
3693 : END DO
3694 4176 : CALL dbcsr_iterator_stop(iter)
3695 : !$OMP END PARALLEL
3696 4176 : IF (numnodes /= 1 .AND. op%redistribute) THEN
3697 4176 : CALL dbcsr_distribute(rpmat)
3698 4176 : IF (.NOT. real_only) CALL dbcsr_distribute(cpmat)
3699 : END IF
3700 :
3701 4176 : CALL timestop(handle)
3702 :
3703 8352 : END SUBROUTINE symtrans_phase
3704 :
3705 : ! **************************************************************************************************
3706 : !> \brief Real two-component representation of the Bloch phase and time reversal.
3707 : !> \param shift column-minus-row cell shift with the selected phase direction
3708 : !> \param xkp target k-point coordinates
3709 : !> \param time_reversal conjugate the source before applying the phase
3710 : !> \param real_only reject phases not representable by real wavefunctions
3711 : !> \param phase coefficients mapping source [Re,Im] to transformed [Re,Im]
3712 : ! **************************************************************************************************
3713 16488397 : SUBROUTINE kp_symmetry_phase_factors(shift, xkp, time_reversal, real_only, phase)
3714 : INTEGER, INTENT(IN) :: shift(3)
3715 : REAL(KIND=dp), INTENT(IN) :: xkp(3)
3716 : LOGICAL, INTENT(IN) :: time_reversal, real_only
3717 : REAL(KIND=dp), INTENT(OUT) :: phase(2, 2)
3718 :
3719 : REAL(KIND=dp) :: arg, cosine, sine
3720 :
3721 16488397 : arg = REAL(shift(1), dp)*xkp(1) + REAL(shift(2), dp)*xkp(2) + REAL(shift(3), dp)*xkp(3)
3722 16488397 : cosine = COS(twopi*arg)
3723 16488397 : sine = SIN(twopi*arg)
3724 16488397 : IF (real_only .AND. ABS(sine) > 1.e-12_dp) THEN
3725 0 : CALL cp_abort(__LOCATION__, "Real k-point wavefunctions cannot represent symmetry phases")
3726 : END IF
3727 49465191 : phase(:, 1) = [cosine, -sine]
3728 49465191 : phase(:, 2) = MERGE(-1.0_dp, 1.0_dp, time_reversal)*[sine, cosine]
3729 16488397 : END SUBROUTINE kp_symmetry_phase_factors
3730 :
3731 : ! **************************************************************************************************
3732 : !> \brief Apply the Bloch phase and optional time reversal to one AO block.
3733 : !> \param rblock real part of source block
3734 : !> \param shift column-minus-row cell shift, with the selected phase direction
3735 : !> \param xkp target k-point coordinates
3736 : !> \param time_reversal conjugate the source before applying the phase
3737 : !> \param rwork real part of the phased block; caller-owned reusable workspace
3738 : !> \param cwork imaginary workspace; absent for real-only wavefunctions
3739 : !> \param cblock imaginary source block; absent when the sparse block is missing
3740 : ! **************************************************************************************************
3741 191496 : SUBROUTINE kp_symmetry_phase_block(rblock, shift, xkp, time_reversal, rwork, cwork, cblock)
3742 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: rblock
3743 : INTEGER, DIMENSION(3), INTENT(IN) :: shift
3744 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xkp
3745 : LOGICAL, INTENT(IN) :: time_reversal
3746 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), &
3747 : INTENT(INOUT) :: rwork
3748 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), &
3749 : INTENT(INOUT), OPTIONAL :: cwork
3750 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
3751 : OPTIONAL :: cblock
3752 :
3753 : REAL(KIND=dp) :: phase(2, 2)
3754 :
3755 191496 : CALL kp_symmetry_phase_factors(shift, xkp, time_reversal,.NOT. PRESENT(cwork), phase)
3756 191496 : CALL ensure_work_matrix(rwork, SIZE(rblock, 1), SIZE(rblock, 2))
3757 10868664 : rwork(:, :) = phase(1, 1)*rblock
3758 191496 : IF (.NOT. PRESENT(cwork)) RETURN
3759 191496 : CALL ensure_work_matrix(cwork, SIZE(rblock, 1), SIZE(rblock, 2))
3760 10868664 : cwork(:, :) = phase(2, 1)*rblock
3761 191496 : IF (PRESENT(cblock)) THEN
3762 10868664 : rwork(:, :) = rwork + phase(1, 2)*cblock
3763 10868664 : cwork(:, :) = cwork + phase(2, 2)*cblock
3764 : END IF
3765 :
3766 : END SUBROUTINE kp_symmetry_phase_block
3767 :
3768 : ! **************************************************************************************************
3769 : !> \brief Accumulate left_rot * op(block) * right_rot^T into one AO block.
3770 : !> \param block phased source block
3771 : !> \param left_rot rotation for the target row
3772 : !> \param right_rot rotation for the target column
3773 : !> \param trans transpose the source when the atom mapping reverses block order
3774 : !> \param alpha sign for real symmetric or imaginary antisymmetric storage
3775 : !> \param TARGET destination block, updated in place
3776 : !> \param work caller-owned reusable GEMM workspace
3777 : ! **************************************************************************************************
3778 47811442 : SUBROUTINE kp_symmetry_rotate_block(block, left_rot, right_rot, trans, alpha, TARGET, work)
3779 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: block, left_rot, right_rot
3780 : LOGICAL, INTENT(IN) :: trans
3781 : REAL(KIND=dp), INTENT(IN) :: alpha
3782 : REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: target
3783 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), &
3784 : INTENT(INOUT) :: work
3785 :
3786 : INTEGER :: ncol
3787 :
3788 47811442 : ncol = MERGE(SIZE(block, 1), SIZE(block, 2), trans)
3789 47811442 : CALL ensure_work_matrix(work, SIZE(left_rot, 1), ncol)
3790 : CALL dgemm('N', MERGE('T', 'N', trans), SIZE(left_rot, 1), ncol, SIZE(left_rot, 2), &
3791 : 1.0_dp, left_rot, SIZE(left_rot, 1), block, SIZE(block, 1), &
3792 90674574 : 0.0_dp, work, SIZE(work, 1))
3793 : CALL dgemm('N', 'T', SIZE(work, 1), SIZE(right_rot, 1), SIZE(work, 2), &
3794 : alpha, work, SIZE(work, 1), right_rot, SIZE(right_rot, 1), &
3795 47811442 : 1.0_dp, TARGET, SIZE(TARGET, 1))
3796 :
3797 47811442 : END SUBROUTINE kp_symmetry_rotate_block
3798 :
3799 25678 : END MODULE kpoint_methods
|