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 K-points and crystal symmetry routines
10 : !> \author jgh
11 : ! **************************************************************************************************
12 : MODULE cryssym
13 :
14 : USE bibliography, ONLY: Togo2018,&
15 : Worlton1972,&
16 : cite_reference
17 : USE kinds, ONLY: dp
18 : USE kpsym, ONLY: group1s,&
19 : k290s
20 : USE mathlib, ONLY: inv_3x3
21 : USE spglib_f08, ONLY: spg_get_international,&
22 : spg_get_major_version,&
23 : spg_get_micro_version,&
24 : spg_get_minor_version,&
25 : spg_get_multiplicity,&
26 : spg_get_pointgroup,&
27 : spg_get_schoenflies,&
28 : spg_get_symmetry
29 : USE string_utilities, ONLY: strip_control_codes
30 : #include "./base/base_uses.f90"
31 :
32 : IMPLICIT NONE
33 : PRIVATE
34 : PUBLIC :: csym_type, release_csym_type, print_crys_symmetry, print_kp_symmetry
35 : PUBLIC :: crys_sym_gen, kpoint_gen
36 :
37 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cryssym'
38 :
39 : ! **************************************************************************************************
40 : !> \brief CSM type
41 : !> \par Content:
42 : !>
43 : ! **************************************************************************************************
44 : TYPE csym_type
45 : LOGICAL :: symlib = .FALSE.
46 : LOGICAL :: fullgrid = .FALSE.
47 : LOGICAL :: inversion_only = .FALSE.
48 : LOGICAL :: spglib_reduction = .FALSE.
49 : LOGICAL :: spglib_backend = .FALSE.
50 : INTEGER :: plevel = 0
51 : INTEGER :: punit = -1
52 : INTEGER :: istriz = -1
53 : REAL(KIND=dp) :: delta = 1.0e-8_dp
54 : REAL(KIND=dp), DIMENSION(3, 3) :: hmat = 0.0_dp
55 : ! KPOINTS
56 : REAL(KIND=dp), DIMENSION(3) :: wvk0 = 0.0_dp
57 : INTEGER, DIMENSION(3) :: mesh = 0
58 : INTEGER :: nkpoint = 0
59 : INTEGER :: nat = 0
60 : INTEGER, DIMENSION(:), ALLOCATABLE :: atype
61 : REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE :: scoord
62 : REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE :: xkpoint
63 : REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: wkpoint
64 : REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE :: kpmesh
65 : INTEGER, DIMENSION(:, :), ALLOCATABLE :: kplink
66 : INTEGER, DIMENSION(:), ALLOCATABLE :: kpop
67 : !SPGLIB
68 : CHARACTER(len=11) :: international_symbol = ""
69 : CHARACTER(len=6) :: pointgroup_symbol = ""
70 : CHARACTER(len=10) :: schoenflies = ""
71 : INTEGER :: n_operations = 0
72 : INTEGER, DIMENSION(:, :, :), ALLOCATABLE :: rotations
73 : REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE :: translations
74 : !K290
75 : REAL(KIND=dp), DIMENSION(:, :, :), ALLOCATABLE :: rt
76 : REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE :: vt
77 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: f0
78 : INTEGER :: nrtot = 0
79 : INTEGER, DIMENSION(:), ALLOCATABLE :: ibrot
80 : END TYPE csym_type
81 :
82 : CONTAINS
83 :
84 : ! **************************************************************************************************
85 : !> \brief Release the CSYM type
86 : !> \param csym The CSYM type
87 : ! **************************************************************************************************
88 1363 : SUBROUTINE release_csym_type(csym)
89 : TYPE(csym_type) :: csym
90 :
91 1363 : IF (ALLOCATED(csym%rotations)) THEN
92 1362 : DEALLOCATE (csym%rotations)
93 : END IF
94 1363 : IF (ALLOCATED(csym%translations)) THEN
95 1362 : DEALLOCATE (csym%translations)
96 : END IF
97 1363 : IF (ALLOCATED(csym%atype)) THEN
98 1363 : DEALLOCATE (csym%atype)
99 : END IF
100 1363 : IF (ALLOCATED(csym%scoord)) THEN
101 1363 : DEALLOCATE (csym%scoord)
102 : END IF
103 1363 : IF (ALLOCATED(csym%xkpoint)) THEN
104 1068 : DEALLOCATE (csym%xkpoint)
105 : END IF
106 1363 : IF (ALLOCATED(csym%wkpoint)) THEN
107 1068 : DEALLOCATE (csym%wkpoint)
108 : END IF
109 1363 : IF (ALLOCATED(csym%kpmesh)) THEN
110 1068 : DEALLOCATE (csym%kpmesh)
111 : END IF
112 1363 : IF (ALLOCATED(csym%kplink)) THEN
113 1068 : DEALLOCATE (csym%kplink)
114 : END IF
115 1363 : IF (ALLOCATED(csym%kpop)) THEN
116 1068 : DEALLOCATE (csym%kpop)
117 : END IF
118 1363 : IF (ALLOCATED(csym%rt)) THEN
119 1068 : DEALLOCATE (csym%rt)
120 : END IF
121 1363 : IF (ALLOCATED(csym%vt)) THEN
122 1068 : DEALLOCATE (csym%vt)
123 : END IF
124 1363 : IF (ALLOCATED(csym%f0)) THEN
125 1068 : DEALLOCATE (csym%f0)
126 : END IF
127 1363 : IF (ALLOCATED(csym%ibrot)) THEN
128 1068 : DEALLOCATE (csym%ibrot)
129 : END IF
130 :
131 1363 : END SUBROUTINE release_csym_type
132 :
133 : ! **************************************************************************************************
134 : !> \brief ...
135 : !> \param csym ...
136 : !> \param scoor ...
137 : !> \param types ...
138 : !> \param hmat ...
139 : !> \param delta ...
140 : !> \param iounit ...
141 : ! **************************************************************************************************
142 1363 : SUBROUTINE crys_sym_gen(csym, scoor, types, hmat, delta, iounit)
143 : TYPE(csym_type) :: csym
144 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: scoor
145 : INTEGER, DIMENSION(:), INTENT(IN) :: types
146 : REAL(KIND=dp), INTENT(IN) :: hmat(3, 3)
147 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: delta
148 : INTEGER, INTENT(IN), OPTIONAL :: iounit
149 :
150 : CHARACTER(LEN=*), PARAMETER :: routineN = 'crys_sym_gen'
151 :
152 : INTEGER :: handle, ierr, major, micro, minor, nat, &
153 : nop, tra_mat(3, 3)
154 : LOGICAL :: spglib
155 :
156 1363 : CALL timeset(routineN, handle)
157 :
158 : !..total number of atoms
159 1363 : nat = SIZE(scoor, 2)
160 1363 : csym%nat = nat
161 :
162 : ! output unit
163 1363 : IF (PRESENT(iounit)) THEN
164 1363 : csym%punit = iounit
165 : ELSE
166 0 : csym%punit = -1
167 : END IF
168 :
169 : ! accuracy for symmetry
170 1363 : IF (PRESENT(delta)) THEN
171 1363 : csym%delta = delta
172 : ELSE
173 0 : csym%delta = 1.e-6_dp
174 : END IF
175 :
176 : !..set cell values
177 17719 : csym%hmat = hmat
178 :
179 : ! atom types
180 4089 : ALLOCATE (csym%atype(nat))
181 11742 : csym%atype(1:nat) = types(1:nat)
182 :
183 : ! scaled coordinates
184 4089 : ALLOCATE (csym%scoord(3, nat))
185 42879 : csym%scoord(1:3, 1:nat) = scoor(1:3, 1:nat)
186 :
187 1363 : csym%n_operations = 0
188 :
189 : !..try spglib
190 1363 : major = spg_get_major_version()
191 1363 : minor = spg_get_minor_version()
192 1363 : micro = spg_get_micro_version()
193 1363 : IF (major == 0) THEN
194 0 : CALL cp_warn(__LOCATION__, "Symmetry library SPGLIB not available")
195 0 : spglib = .FALSE.
196 : ELSE
197 1363 : spglib = .TRUE.
198 1363 : CALL cite_reference(Togo2018)
199 1363 : ierr = spg_get_international(csym%international_symbol, TRANSPOSE(hmat), scoor, types, nat, delta)
200 1363 : IF (ierr == 0) THEN
201 1 : CALL cp_warn(__LOCATION__, "Symmetry Library SPGLIB failed")
202 1 : spglib = .FALSE.
203 : ELSE
204 1362 : nop = spg_get_multiplicity(TRANSPOSE(hmat), scoor, types, nat, delta)
205 6810 : ALLOCATE (csym%rotations(3, 3, nop), csym%translations(3, nop))
206 1362 : csym%n_operations = nop
207 : ierr = spg_get_symmetry(csym%rotations, csym%translations, nop, &
208 1362 : TRANSPOSE(hmat), scoor, types, nat, delta)
209 : ! Schoenflies Symbol
210 1362 : csym%schoenflies = ' '
211 1362 : ierr = spg_get_schoenflies(csym%schoenflies, TRANSPOSE(hmat), scoor, types, nat, delta)
212 : ! Point Group
213 1362 : csym%pointgroup_symbol = ' '
214 1362 : tra_mat = 0
215 : ierr = spg_get_pointgroup(csym%pointgroup_symbol, tra_mat, &
216 1362 : csym%rotations, csym%n_operations)
217 :
218 1362 : CALL strip_control_codes(csym%international_symbol)
219 1362 : CALL strip_control_codes(csym%schoenflies)
220 1362 : CALL strip_control_codes(csym%pointgroup_symbol)
221 : END IF
222 : END IF
223 1363 : csym%symlib = spglib
224 :
225 1363 : CALL timestop(handle)
226 :
227 1363 : END SUBROUTINE crys_sym_gen
228 :
229 : ! **************************************************************************************************
230 : !> \brief ...
231 : !> \param csym ...
232 : !> \param nk ...
233 : !> \param symm ...
234 : !> \param shift ...
235 : !> \param full_grid ...
236 : !> \param inversion_symmetry_only ...
237 : !> \param use_spglib_reduction ...
238 : !> \param use_spglib_backend ...
239 : ! **************************************************************************************************
240 1068 : SUBROUTINE kpoint_gen(csym, nk, symm, shift, full_grid, inversion_symmetry_only, &
241 : use_spglib_reduction, use_spglib_backend)
242 : TYPE(csym_type) :: csym
243 : INTEGER, INTENT(IN) :: nk(3)
244 : LOGICAL, INTENT(IN), OPTIONAL :: symm
245 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: shift(3)
246 : LOGICAL, INTENT(IN), OPTIONAL :: full_grid, inversion_symmetry_only, &
247 : use_spglib_reduction, &
248 : use_spglib_backend
249 :
250 : CHARACTER(LEN=*), PARAMETER :: routineN = 'kpoint_gen'
251 :
252 : INTEGER :: handle, i, ik, j, nkp, nkpts
253 1068 : INTEGER, ALLOCATABLE, DIMENSION(:) :: kpop, xptr
254 : LOGICAL :: fullmesh, inversion_only, &
255 : spglib_backend, spglib_reduction
256 1068 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: wkp
257 1068 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: xkp
258 :
259 1068 : CALL timeset(routineN, handle)
260 :
261 1068 : IF (PRESENT(shift)) THEN
262 4272 : csym%wvk0 = shift
263 : ELSE
264 0 : csym%wvk0 = 0.0_dp
265 : END IF
266 :
267 1068 : csym%istriz = -1
268 1068 : IF (PRESENT(symm)) THEN
269 1068 : IF (symm) csym%istriz = 1
270 : END IF
271 :
272 1068 : IF (PRESENT(full_grid)) THEN
273 1068 : fullmesh = full_grid
274 : ELSE
275 : fullmesh = .FALSE.
276 : END IF
277 1068 : csym%fullgrid = fullmesh
278 :
279 1068 : IF (PRESENT(inversion_symmetry_only)) THEN
280 1068 : inversion_only = inversion_symmetry_only
281 : ELSE
282 : inversion_only = .FALSE.
283 : END IF
284 1068 : csym%inversion_only = inversion_only
285 :
286 1068 : IF (PRESENT(use_spglib_reduction)) THEN
287 1068 : spglib_reduction = use_spglib_reduction
288 : ELSE
289 0 : spglib_reduction = .FALSE.
290 : END IF
291 1068 : csym%spglib_reduction = spglib_reduction
292 :
293 1068 : IF (PRESENT(use_spglib_backend)) THEN
294 1068 : spglib_backend = use_spglib_backend
295 : ELSE
296 : spglib_backend = .FALSE.
297 : END IF
298 1068 : csym%spglib_backend = spglib_backend
299 :
300 1068 : IF (spglib_backend .AND. .NOT. spglib_reduction) THEN
301 : CALL cp_abort(__LOCATION__, &
302 0 : "SYMMETRY_BACKEND SPGLIB requires SYMMETRY_REDUCTION_METHOD SPGLIB")
303 : END IF
304 :
305 1068 : csym%nkpoint = 0
306 4272 : csym%mesh(1:3) = nk(1:3)
307 1068 : csym%nrtot = 0
308 1068 : IF (ALLOCATED(csym%rt)) DEALLOCATE (csym%rt)
309 1068 : IF (ALLOCATED(csym%vt)) DEALLOCATE (csym%vt)
310 1068 : IF (ALLOCATED(csym%ibrot)) DEALLOCATE (csym%ibrot)
311 1068 : IF (ALLOCATED(csym%f0)) DEALLOCATE (csym%f0)
312 2136 : ALLOCATE (csym%rt(3, 3, 0), csym%vt(3, 0), csym%ibrot(0), csym%f0(csym%nat, 0))
313 :
314 1068 : nkpts = nk(1)*nk(2)*nk(3)
315 7476 : ALLOCATE (xkp(3, nkpts), wkp(nkpts), kpop(nkpts))
316 : ! kp: link
317 3204 : ALLOCATE (csym%kplink(2, nkpts))
318 42300 : csym%kplink = 0
319 :
320 : ! go through all the options
321 1068 : IF (csym%symlib) THEN
322 : ! symmetry library is available
323 1068 : IF (fullmesh) THEN
324 : ! full mesh requested
325 318 : CALL full_grid_gen(nk, xkp, wkp, shift)
326 318 : IF (csym%istriz == 1) THEN
327 : ! use inversion symmetry
328 292 : CALL inversion_symm(xkp, wkp, csym%kplink(1, :))
329 : ELSE
330 : ! full kpoint mesh is used
331 : END IF
332 750 : ELSE IF (csym%istriz /= 1 .OR. inversion_only) THEN
333 : ! use inversion symmetry
334 656 : CALL full_grid_gen(nk, xkp, wkp, shift)
335 656 : CALL inversion_symm(xkp, wkp, csym%kplink(1, :))
336 : ELSE
337 : ! use symmetry library to reduce k-points
338 376 : IF (SUM(ABS(csym%wvk0)) /= 0.0_dp) THEN
339 : CALL cp_abort(__LOCATION__, "MacDonald shifted k-point meshes are only "// &
340 0 : "possible without symmetrization.")
341 : END IF
342 :
343 94 : CALL full_grid_gen(nk, xkp, wkp, shift)
344 94 : IF (spglib_backend) THEN
345 22 : CALL kp_symmetry_spglib(csym, xkp, wkp, kpop)
346 : ELSE
347 72 : CALL kp_symmetry(csym, xkp, wkp, kpop, use_spglib_reduction=spglib_reduction)
348 : END IF
349 :
350 : END IF
351 : ELSE
352 : ! no symmetry library is available
353 0 : CALL full_grid_gen(nk, xkp, wkp, shift)
354 0 : IF (csym%istriz /= 1 .AND. fullmesh) THEN
355 : ! full kpoint mesh is used
356 0 : DO i = 1, nkpts
357 0 : csym%kplink(1, i) = i
358 : END DO
359 : ELSE
360 : ! use inversion symmetry
361 0 : CALL inversion_symm(xkp, wkp, csym%kplink(1, :))
362 : END IF
363 : END IF
364 : ! count kpoints
365 1068 : nkp = 0
366 14812 : DO i = 1, nkpts
367 14812 : IF (wkp(i) > 0.0_dp) nkp = nkp + 1
368 : END DO
369 :
370 : ! store reduced kpoint set
371 1068 : csym%nkpoint = nkp
372 5340 : ALLOCATE (csym%xkpoint(3, nkp), csym%wkpoint(nkp))
373 3204 : ALLOCATE (xptr(nkp))
374 14812 : j = 0
375 14812 : DO ik = 1, nkpts
376 14812 : IF (wkp(ik) > 0.0_dp) THEN
377 5986 : j = j + 1
378 5986 : csym%wkpoint(j) = wkp(ik)
379 23944 : csym%xkpoint(1:3, j) = xkp(1:3, ik)
380 5986 : xptr(j) = ik
381 : END IF
382 : END DO
383 1068 : CPASSERT(j == nkp)
384 :
385 : ! kp: mesh
386 2136 : ALLOCATE (csym%kpmesh(3, nkpts))
387 56044 : csym%kpmesh(1:3, 1:nkpts) = xkp(1:3, 1:nkpts)
388 :
389 : ! kp: link
390 14812 : DO ik = 1, nkpts
391 13744 : i = csym%kplink(1, ik)
392 122106 : DO j = 1, nkp
393 121038 : IF (i == xptr(j)) THEN
394 13500 : csym%kplink(2, ik) = j
395 13500 : EXIT
396 : END IF
397 : END DO
398 : END DO
399 1068 : DEALLOCATE (xptr)
400 :
401 : ! kp: operations
402 2136 : ALLOCATE (csym%kpop(nkpts))
403 1068 : IF (csym%symlib .AND. csym%istriz == 1 .AND. .NOT. fullmesh .AND. .NOT. inversion_only) THEN
404 : ! atomic symmetry operations possible
405 2748 : csym%kpop(1:nkpts) = kpop(1:nkpts)
406 2748 : DO ik = 1, nkpts
407 2748 : CPASSERT(csym%kpop(ik) /= 0)
408 : END DO
409 : ELSE
410 : ! only time reversal symmetry
411 12064 : DO ik = 1, nkpts
412 12064 : IF (wkp(ik) > 0.0_dp) THEN
413 5686 : csym%kpop(ik) = 1
414 : ELSE
415 5404 : csym%kpop(ik) = 2
416 : END IF
417 : END DO
418 : END IF
419 :
420 1068 : DEALLOCATE (xkp, wkp, kpop)
421 :
422 1068 : CALL timestop(handle)
423 :
424 1068 : END SUBROUTINE kpoint_gen
425 :
426 : ! **************************************************************************************************
427 : !> \brief ...
428 : !> \param csym ...
429 : !> \param xkp ...
430 : !> \param wkp ...
431 : !> \param kpop ...
432 : !> \param use_spglib_reduction ...
433 : ! **************************************************************************************************
434 72 : SUBROUTINE kp_symmetry(csym, xkp, wkp, kpop, use_spglib_reduction)
435 : TYPE(csym_type) :: csym
436 : REAL(KIND=dp), DIMENSION(:, :) :: xkp
437 : REAL(KIND=dp), DIMENSION(:) :: wkp
438 : INTEGER, DIMENSION(:) :: kpop
439 : LOGICAL, INTENT(IN), OPTIONAL :: use_spglib_reduction
440 :
441 : INTEGER :: i, ihc, ihg, indpg, iou, iq1, iq2, iq3, &
442 : istriz, isy, li, nat, nc, nhash, &
443 : nkpoint, nrot, nsp, ntvec
444 72 : INTEGER, ALLOCATABLE, DIMENSION(:) :: includ, isc, list, lwght, ty
445 72 : INTEGER, ALLOCATABLE, DIMENSION(:, :) :: f0, lrot
446 72 : INTEGER, ALLOCATABLE, DIMENSION(:, :, :) :: srot
447 : INTEGER, DIMENSION(48) :: ib
448 : LOGICAL :: spglib_reduction
449 : REAL(KIND=dp) :: alat
450 72 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: rlist, rx, tvec, wvkl, xkapa
451 : REAL(KIND=dp), DIMENSION(3) :: a1, a2, a3, b1, b2, b3, origin, wvk0
452 : REAL(KIND=dp), DIMENSION(3, 3) :: hmat, strain
453 : REAL(KIND=dp), DIMENSION(3, 3, 48) :: r
454 : REAL(KIND=dp), DIMENSION(3, 48) :: vt
455 :
456 72 : iou = csym%punit
457 936 : hmat = csym%hmat
458 72 : nat = csym%nat
459 72 : iq1 = csym%mesh(1)
460 72 : iq2 = csym%mesh(2)
461 72 : iq3 = csym%mesh(3)
462 72 : nkpoint = 10*iq1*iq2*iq3
463 288 : wvk0 = csym%wvk0
464 72 : istriz = csym%istriz
465 72 : IF (PRESENT(use_spglib_reduction)) THEN
466 72 : spglib_reduction = use_spglib_reduction
467 : ELSE
468 : spglib_reduction = .FALSE.
469 : END IF
470 288 : a1(1:3) = hmat(1:3, 1)
471 288 : a2(1:3) = hmat(1:3, 2)
472 288 : a3(1:3) = hmat(1:3, 3)
473 72 : alat = hmat(1, 1)
474 72 : strain = 0.0_dp
475 648 : ALLOCATE (xkapa(3, nat), rx(3, nat), tvec(3, 200), ty(nat), isc(nat), f0(49, nat))
476 588 : ty(1:nat) = csym%atype(1:nat)
477 588 : nsp = MAXVAL(ty)
478 588 : DO i = 1, nat
479 8844 : xkapa(1:3, i) = MATMUL(hmat, csym%scoord(1:3, i))
480 : END DO
481 72 : nhash = 1000
482 576 : ALLOCATE (wvkl(3, nkpoint), rlist(3, nkpoint), includ(nkpoint), list(nhash + nkpoint))
483 288 : ALLOCATE (lrot(48, nkpoint), lwght(nkpoint))
484 :
485 72 : IF (iou > 0) THEN
486 : WRITE (iou, '(/,(T2,A79))') &
487 25 : "*******************************************************************************", &
488 25 : "** Special K-Point Generation by K290 **", &
489 50 : "*******************************************************************************"
490 : END IF
491 72 : CALL cite_reference(Worlton1972)
492 72 : IF (spglib_reduction) CALL cite_reference(Togo2018)
493 :
494 : CALL K290s(iou, nat, nkpoint, nsp, iq1, iq2, iq3, istriz, &
495 : a1, a2, a3, alat, strain, xkapa, rx, tvec, &
496 : ty, isc, f0, ntvec, wvk0, wvkl, lwght, lrot, &
497 72 : nhash, includ, list, rlist, csym%delta)
498 :
499 : CALL GROUP1s(0, a1, a2, a3, nat, ty, xkapa, b1, b2, b3, &
500 : ihg, ihc, isy, li, nc, indpg, ib, ntvec, &
501 72 : vt, f0, r, tvec, origin, rx, isc, csym%delta)
502 :
503 72 : IF (iou > 0) THEN
504 : WRITE (iou, '((T2,A79))') &
505 25 : "*******************************************************************************", &
506 25 : "** Finished K290 **", &
507 50 : "*******************************************************************************"
508 : END IF
509 :
510 72 : csym%nrtot = nc
511 72 : IF (ALLOCATED(csym%rt)) DEALLOCATE (csym%rt)
512 72 : IF (ALLOCATED(csym%vt)) DEALLOCATE (csym%vt)
513 72 : IF (ALLOCATED(csym%ibrot)) DEALLOCATE (csym%ibrot)
514 72 : IF (ALLOCATED(csym%f0)) DEALLOCATE (csym%f0)
515 504 : ALLOCATE (csym%rt(3, 3, nc), csym%vt(3, nc), csym%ibrot(nc))
516 7408 : csym%vt(1:3, 1:nc) = vt(1:3, 1:nc)
517 288 : ALLOCATE (csym%f0(nat, nc))
518 1906 : DO i = 1, nc
519 23842 : csym%rt(1:3, 1:3, i) = r(1:3, 1:3, ib(i))
520 16450 : csym%f0(1:nat, i) = f0(i, 1:nat)
521 : END DO
522 1906 : csym%ibrot(1:nc) = ib(1:nc)
523 :
524 72 : IF (spglib_reduction) THEN
525 30 : ALLOCATE (srot(3, 3, csym%n_operations))
526 10 : CALL setup_spglib_reduction_rotations(csym, srot, nrot)
527 : CALL reduce_spglib_kpoint_mesh_k290(csym, xkp, wkp, kpop, srot, nrot, &
528 10 : a1, a2, a3, b1, b2, b3, alat)
529 20 : DEALLOCATE (srot)
530 : ELSE
531 62 : CALL reduce_kpoint_mesh(csym, xkp, wkp, kpop, nc, ib, r, a1, a2, a3, b1, b2, b3, alat)
532 : END IF
533 72 : DEALLOCATE (xkapa, rx, tvec, ty, isc, f0)
534 72 : DEALLOCATE (wvkl, rlist, includ, list)
535 72 : DEALLOCATE (lrot, lwght)
536 :
537 72 : END SUBROUTINE kp_symmetry
538 :
539 : ! **************************************************************************************************
540 : !> \brief Reduce a CP2K Monkhorst-Pack mesh using SPGLIB symmetry operations
541 : !> \param csym ...
542 : !> \param xkp ...
543 : !> \param wkp ...
544 : !> \param kpop ...
545 : ! **************************************************************************************************
546 22 : SUBROUTINE kp_symmetry_spglib(csym, xkp, wkp, kpop)
547 : TYPE(csym_type) :: csym
548 : REAL(KIND=dp), DIMENSION(:, :) :: xkp
549 : REAL(KIND=dp), DIMENSION(:) :: wkp
550 : INTEGER, DIMENSION(:) :: kpop
551 :
552 : INTEGER :: iou, nrot
553 22 : INTEGER, ALLOCATABLE, DIMENSION(:, :, :) :: srot
554 :
555 22 : iou = csym%punit
556 22 : IF (iou > 0) THEN
557 : WRITE (iou, '(/,(T2,A79))') &
558 10 : "*******************************************************************************", &
559 10 : "** Special K-Point Generation by SPGLIB **", &
560 20 : "*******************************************************************************"
561 : END IF
562 22 : CALL cite_reference(Togo2018)
563 :
564 66 : ALLOCATE (srot(3, 3, csym%n_operations))
565 22 : CALL setup_spglib_operations(csym, srot, nrot)
566 22 : CALL reduce_spglib_kpoint_mesh(csym, xkp, wkp, kpop, srot, nrot)
567 22 : DEALLOCATE (srot)
568 :
569 22 : IF (iou > 0) THEN
570 : WRITE (iou, '((T2,A79))') &
571 10 : "*******************************************************************************", &
572 10 : "** Finished SPGLIB **", &
573 20 : "*******************************************************************************"
574 : END IF
575 :
576 22 : END SUBROUTINE kp_symmetry_spglib
577 :
578 : ! **************************************************************************************************
579 : !> \brief Store usable SPGLIB space-group operations for k-point symmetry
580 : !> \param csym ...
581 : !> \param srot integer rotations in fractional coordinates
582 : !> \param nrot number of stored rotations
583 : ! **************************************************************************************************
584 22 : SUBROUTINE setup_spglib_operations(csym, srot, nrot)
585 : TYPE(csym_type) :: csym
586 : INTEGER, DIMENSION(:, :, :), INTENT(OUT) :: srot
587 : INTEGER, INTENT(OUT) :: nrot
588 :
589 : INTEGER :: iop, jop, pass
590 22 : INTEGER, ALLOCATABLE, DIMENSION(:) :: perm
591 : INTEGER, DIMENSION(3, 3) :: eye, irot
592 : LOGICAL :: duplicate, identity, valid, &
593 : zero_translation
594 : REAL(KIND=dp) :: eps
595 : REAL(KIND=dp), DIMENSION(3, 3) :: h_inv, rfrac
596 :
597 22 : CPASSERT(csym%symlib)
598 :
599 50020 : srot = 0
600 22 : csym%nrtot = 0
601 22 : IF (ALLOCATED(csym%rt)) DEALLOCATE (csym%rt)
602 22 : IF (ALLOCATED(csym%vt)) DEALLOCATE (csym%vt)
603 22 : IF (ALLOCATED(csym%ibrot)) DEALLOCATE (csym%ibrot)
604 22 : IF (ALLOCATED(csym%f0)) DEALLOCATE (csym%f0)
605 110 : ALLOCATE (csym%rt(3, 3, csym%n_operations), csym%vt(3, csym%n_operations))
606 132 : ALLOCATE (csym%ibrot(csym%n_operations), csym%f0(csym%nat, csym%n_operations))
607 50020 : csym%rt = 0.0_dp
608 15406 : csym%vt = 0.0_dp
609 3868 : csym%ibrot = 0
610 34696 : csym%f0 = 0
611 :
612 22 : eps = MAX(1.e-12_dp, 10.0_dp*csym%delta)
613 22 : h_inv = inv_3x3(csym%hmat)
614 66 : ALLOCATE (perm(csym%nat))
615 :
616 22 : eye = 0
617 22 : eye(1, 1) = 1
618 22 : eye(2, 2) = 1
619 22 : eye(3, 3) = 1
620 :
621 22 : nrot = 0
622 : ! Operation 1 is used as the untransformed representative k-point.
623 : ! Prefer integer translations before fractional alternatives with the same rotation.
624 110 : DO pass = 1, 4
625 15494 : DO iop = 1, csym%n_operations
626 199992 : irot(1:3, 1:3) = csym%rotations(1:3, 1:3, iop)
627 32120 : identity = ALL(irot == eye)
628 : zero_translation = ALL(ABS(csym%translations(1:3, iop) - &
629 23136 : ANINT(csym%translations(1:3, iop))) <= eps)
630 15384 : IF (pass == 1 .AND. (.NOT. identity .OR. .NOT. zero_translation)) CYCLE
631 11560 : IF (pass == 2 .AND. (identity .OR. .NOT. zero_translation)) CYCLE
632 8178 : IF (pass == 3 .AND. (.NOT. identity .OR. zero_translation)) CYCLE
633 4392 : IF (pass == 4 .AND. (identity .OR. zero_translation)) CYCLE
634 :
635 3846 : duplicate = .FALSE.
636 370570 : DO jop = 1, nrot
637 1036816 : IF (ALL(irot == srot(:, :, jop)) .AND. &
638 : ALL(ABS(csym%translations(1:3, iop) - csym%vt(1:3, jop) - &
639 3846 : ANINT(csym%translations(1:3, iop) - csym%vt(1:3, jop))) <= eps)) THEN
640 : duplicate = .TRUE.
641 : EXIT
642 : END IF
643 : END DO
644 3846 : IF (duplicate) CYCLE
645 :
646 3846 : CALL spglib_atom_permutation(csym, irot, csym%translations(:, iop), perm, valid)
647 3846 : IF (.NOT. valid) CYCLE
648 :
649 3842 : nrot = nrot + 1
650 :
651 49946 : srot(1:3, 1:3, nrot) = irot(1:3, 1:3)
652 49946 : rfrac(1:3, 1:3) = REAL(irot(1:3, 1:3), KIND=dp)
653 349622 : csym%rt(1:3, 1:3, nrot) = MATMUL(csym%hmat, MATMUL(rfrac, h_inv))
654 15368 : csym%vt(1:3, nrot) = csym%translations(1:3, iop)
655 3842 : csym%ibrot(nrot) = nrot
656 34686 : csym%f0(1:csym%nat, nrot) = perm(1:csym%nat)
657 : END DO
658 : END DO
659 :
660 22 : DEALLOCATE (perm)
661 22 : csym%nrtot = nrot
662 22 : IF (nrot == 0) CALL cp_abort(__LOCATION__, "SPGLIB did not return usable symmetry operations")
663 :
664 22 : END SUBROUTINE setup_spglib_operations
665 :
666 : ! **************************************************************************************************
667 : !> \brief Store unique SPGLIB rotations for K290-backend diagnostic reduction
668 : !> \param csym ...
669 : !> \param srot integer rotations in fractional coordinates
670 : !> \param nrot number of stored rotations
671 : ! **************************************************************************************************
672 10 : SUBROUTINE setup_spglib_reduction_rotations(csym, srot, nrot)
673 : TYPE(csym_type) :: csym
674 : INTEGER, DIMENSION(:, :, :), INTENT(OUT) :: srot
675 : INTEGER, INTENT(OUT) :: nrot
676 :
677 : INTEGER :: iop, jop, pass
678 : INTEGER, DIMENSION(3, 3) :: eye, irot
679 : LOGICAL :: duplicate, identity
680 :
681 10 : CPASSERT(csym%symlib)
682 :
683 10306 : srot = 0
684 10 : eye = 0
685 10 : eye(1, 1) = 1
686 10 : eye(2, 2) = 1
687 10 : eye(3, 3) = 1
688 :
689 10 : nrot = 0
690 : ! Keep the identity first, matching the representative k-point operation.
691 30 : DO pass = 1, 2
692 1614 : DO iop = 1, csym%n_operations
693 20592 : irot(1:3, 1:3) = csym%rotations(1:3, 1:3, iop)
694 3572 : identity = ALL(irot == eye)
695 1584 : IF (pass == 1 .AND. .NOT. identity) CYCLE
696 814 : IF (pass == 2 .AND. identity) CYCLE
697 :
698 792 : duplicate = .FALSE.
699 18876 : DO jop = 1, nrot
700 47094 : IF (ALL(irot == srot(:, :, jop))) THEN
701 : duplicate = .TRUE.
702 : EXIT
703 : END IF
704 : END DO
705 792 : IF (duplicate) CYCLE
706 :
707 216 : nrot = nrot + 1
708 3426 : srot(1:3, 1:3, nrot) = irot(1:3, 1:3)
709 : END DO
710 : END DO
711 :
712 10 : IF (nrot == 0) CALL cp_abort(__LOCATION__, "SPGLIB did not return usable symmetry rotations")
713 :
714 10 : END SUBROUTINE setup_spglib_reduction_rotations
715 :
716 : ! **************************************************************************************************
717 : !> \brief Determine the atom permutation generated by a SPGLIB space-group operation
718 : !> \param csym ...
719 : !> \param rot integer rotation in fractional coordinates
720 : !> \param trans fractional translation
721 : !> \param perm atom permutation
722 : !> \param valid whether all atoms were mapped
723 : ! **************************************************************************************************
724 3846 : SUBROUTINE spglib_atom_permutation(csym, rot, trans, perm, valid)
725 : TYPE(csym_type) :: csym
726 : INTEGER, DIMENSION(3, 3), INTENT(IN) :: rot
727 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: trans
728 : INTEGER, DIMENSION(:), INTENT(OUT) :: perm
729 : LOGICAL, INTENT(OUT) :: valid
730 :
731 : INTEGER :: i, j, nat
732 : LOGICAL :: found
733 3846 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: used
734 : REAL(KIND=dp) :: eps
735 : REAL(KIND=dp), DIMENSION(3) :: diff, spos
736 : REAL(KIND=dp), DIMENSION(3, 3) :: rfrac
737 :
738 3846 : nat = csym%nat
739 3846 : eps = MAX(1.e-12_dp, 10.0_dp*csym%delta)
740 49998 : rfrac(1:3, 1:3) = REAL(rot(1:3, 1:3), KIND=dp)
741 11538 : ALLOCATE (used(nat))
742 34674 : used = .FALSE.
743 34674 : perm = 0
744 3846 : valid = .TRUE.
745 :
746 34602 : DO i = 1, nat
747 492160 : spos(1:3) = MATMUL(rfrac(1:3, 1:3), csym%scoord(1:3, i)) + trans(1:3)
748 138658 : found = .FALSE.
749 138658 : DO j = 1, nat
750 138654 : IF (used(j)) CYCLE
751 84588 : IF (csym%atype(i) /= csym%atype(j)) CYCLE
752 338352 : diff(1:3) = spos(1:3) - csym%scoord(1:3, j)
753 338352 : diff(1:3) = diff(1:3) - ANINT(diff(1:3))
754 184540 : IF (ALL(ABS(diff(1:3)) < eps)) THEN
755 30756 : perm(i) = j
756 30756 : used(j) = .TRUE.
757 : found = .TRUE.
758 : EXIT
759 : END IF
760 : END DO
761 3842 : IF (.NOT. found) THEN
762 4 : valid = .FALSE.
763 4 : EXIT
764 : END IF
765 : END DO
766 :
767 3846 : DEALLOCATE (used)
768 :
769 3846 : END SUBROUTINE spglib_atom_permutation
770 :
771 : ! **************************************************************************************************
772 : !> \brief Reduce a k-point mesh with SPGLIB direct-space operations
773 : !> \param csym ...
774 : !> \param xkp full k-point mesh in reciprocal lattice coordinates
775 : !> \param wkp reduced k-point weights
776 : !> \param kpop symmetry operation mapping the representative k-point to a mesh point
777 : !> \param srot integer rotations in fractional coordinates
778 : !> \param nrot number of stored rotations
779 : ! **************************************************************************************************
780 22 : SUBROUTINE reduce_spglib_kpoint_mesh(csym, xkp, wkp, kpop, srot, nrot)
781 : TYPE(csym_type) :: csym
782 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: xkp
783 : REAL(KIND=dp), DIMENSION(:) :: wkp
784 : INTEGER, DIMENSION(:) :: kpop
785 : INTEGER, DIMENSION(:, :, :), INTENT(IN) :: srot
786 : INTEGER, INTENT(IN) :: nrot
787 :
788 : INTEGER :: i, iop, isign, j, kr, nkpts, score
789 22 : INTEGER, ALLOCATABLE, DIMENSION(:) :: kscore
790 : INTEGER, DIMENSION(3, 3) :: krot
791 : REAL(KIND=dp) :: eps
792 : REAL(KIND=dp), DIMENSION(3) :: diff, rr
793 :
794 22 : nkpts = SIZE(wkp)
795 22 : eps = MAX(1.e-12_dp, 10.0_dp*csym%delta)
796 66 : ALLOCATE (kscore(nkpts))
797 :
798 764 : wkp = 0.0_dp
799 764 : kpop = 0
800 764 : csym%kplink(1, :) = 0
801 764 : kscore = HUGE(0)
802 :
803 764 : DO i = 1, nkpts
804 742 : IF (csym%kplink(1, i) /= 0) CYCLE
805 :
806 68 : csym%kplink(1, i) = i
807 68 : wkp(i) = 1.0_dp
808 68 : kpop(i) = 1
809 68 : kscore(i) = 0
810 :
811 9708 : DO iop = 1, nrot
812 9618 : kr = csym%ibrot(iop)
813 9618 : krot = reciprocal_rotation(srot(:, :, kr))
814 9618 : score = spglib_operation_score(csym, iop, srot(:, :, kr))
815 29596 : DO isign = 1, 2
816 480900 : rr(1:3) = MATMUL(REAL(krot(1:3, 1:3), KIND=dp), xkp(1:3, i))
817 19236 : IF (isign == 2) THEN
818 38472 : rr(1:3) = -rr(1:3)
819 9618 : kr = -csym%ibrot(iop)
820 : ELSE
821 9618 : kr = csym%ibrot(iop)
822 : END IF
823 :
824 514134 : DO j = 1, nkpts
825 2056536 : diff(1:3) = xkp(1:3, j) - rr(1:3)
826 2056536 : diff(1:3) = diff(1:3) - ANINT(diff(1:3))
827 716334 : IF (ALL(ABS(diff(1:3)) < eps)) THEN
828 19236 : IF (csym%kplink(1, j) == 0) THEN
829 674 : csym%kplink(1, j) = i
830 674 : wkp(i) = wkp(i) + 1.0_dp
831 674 : kpop(j) = kr
832 674 : kscore(j) = score
833 : ELSE
834 18562 : CPASSERT(csym%kplink(1, j) == i)
835 18562 : IF (score < kscore(j)) THEN
836 216 : kpop(j) = kr
837 216 : kscore(j) = score
838 : END IF
839 : END IF
840 : EXIT
841 : END IF
842 : END DO
843 28854 : IF (j > nkpts) CYCLE
844 : END DO
845 : END DO
846 : END DO
847 :
848 764 : DO i = 1, nkpts
849 742 : CPASSERT(csym%kplink(1, i) /= 0)
850 764 : CPASSERT(kpop(i) /= 0)
851 : END DO
852 22 : DEALLOCATE (kscore)
853 :
854 22 : END SUBROUTINE reduce_spglib_kpoint_mesh
855 :
856 : ! **************************************************************************************************
857 : !> \brief Reduce a k-point mesh with SPGLIB rotations and K290 operations
858 : !> \param csym ...
859 : !> \param xkp full k-point mesh in reciprocal lattice coordinates
860 : !> \param wkp reduced k-point weights
861 : !> \param kpop K290 operation mapping the representative k-point to a mesh point
862 : !> \param srot SPGLIB integer rotations in fractional coordinates
863 : !> \param nrot number of stored rotations
864 : !> \param a1 first lattice vector
865 : !> \param a2 second lattice vector
866 : !> \param a3 third lattice vector
867 : !> \param b1 first reciprocal lattice vector
868 : !> \param b2 second reciprocal lattice vector
869 : !> \param b3 third reciprocal lattice vector
870 : !> \param alat lattice scaling used by K290
871 : ! **************************************************************************************************
872 10 : SUBROUTINE reduce_spglib_kpoint_mesh_k290(csym, xkp, wkp, kpop, srot, nrot, &
873 : a1, a2, a3, b1, b2, b3, alat)
874 : TYPE(csym_type) :: csym
875 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: xkp
876 : REAL(KIND=dp), DIMENSION(:) :: wkp
877 : INTEGER, DIMENSION(:) :: kpop
878 : INTEGER, DIMENSION(:, :, :), INTENT(IN) :: srot
879 : INTEGER, INTENT(IN) :: nrot
880 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: a1, a2, a3, b1, b2, b3
881 : REAL(KIND=dp), INTENT(IN) :: alat
882 :
883 : INTEGER :: i, iop, isign, j, k290_op, nkpts
884 : INTEGER, DIMENSION(3, 3) :: krot
885 : LOGICAL :: valid
886 : REAL(KIND=dp) :: eps
887 : REAL(KIND=dp), DIMENSION(3) :: diff, rr
888 :
889 10 : nkpts = SIZE(wkp)
890 10 : eps = MAX(1.e-12_dp, 10.0_dp*csym%delta)
891 :
892 202 : wkp = 0.0_dp
893 202 : kpop = 0
894 202 : csym%kplink(1, :) = 0
895 :
896 202 : DO i = 1, nkpts
897 192 : IF (csym%kplink(1, i) /= 0) CYCLE
898 :
899 36 : csym%kplink(1, i) = i
900 36 : wkp(i) = 1.0_dp
901 36 : kpop(i) = 1
902 :
903 366 : DO iop = 1, nrot
904 320 : krot = reciprocal_rotation(srot(:, :, iop))
905 1152 : DO isign = 1, 2
906 16000 : rr(1:3) = MATMUL(REAL(krot(1:3, 1:3), KIND=dp), xkp(1:3, i))
907 1600 : IF (isign == 2) rr(1:3) = -rr(1:3)
908 :
909 8256 : DO j = 1, nkpts
910 33024 : diff(1:3) = xkp(1:3, j) - rr(1:3)
911 33024 : diff(1:3) = diff(1:3) - ANINT(diff(1:3))
912 12904 : IF (ALL(ABS(diff(1:3)) < eps)) THEN
913 640 : IF (j == i) EXIT
914 536 : IF (csym%kplink(1, j) /= 0) THEN
915 380 : CPASSERT(csym%kplink(1, j) == i)
916 : EXIT
917 : END IF
918 :
919 : CALL find_k290_kpoint_operation(csym, xkp(1:3, i), xkp(1:3, j), &
920 : a1, a2, a3, b1, b2, b3, alat, &
921 156 : k290_op, valid)
922 156 : IF (.NOT. valid) THEN
923 : CALL cp_abort(__LOCATION__, &
924 0 : "SPGLIB k-point mapping is not represented by K290 backend")
925 : END IF
926 156 : csym%kplink(1, j) = i
927 156 : wkp(i) = wkp(i) + 1.0_dp
928 156 : kpop(j) = k290_op
929 156 : EXIT
930 : END IF
931 : END DO
932 960 : IF (j > nkpts) CYCLE
933 : END DO
934 : END DO
935 : END DO
936 :
937 202 : DO i = 1, nkpts
938 192 : CPASSERT(csym%kplink(1, i) /= 0)
939 202 : CPASSERT(kpop(i) /= 0)
940 : END DO
941 :
942 10 : END SUBROUTINE reduce_spglib_kpoint_mesh_k290
943 :
944 : ! **************************************************************************************************
945 : !> \brief Find a K290 operation that maps one fractional k-point to another
946 : !> \param csym ...
947 : !> \param xref representative k-point
948 : !> \param xtarget target k-point
949 : !> \param a1 first lattice vector
950 : !> \param a2 second lattice vector
951 : !> \param a3 third lattice vector
952 : !> \param b1 first reciprocal lattice vector
953 : !> \param b2 second reciprocal lattice vector
954 : !> \param b3 third reciprocal lattice vector
955 : !> \param alat lattice scaling used by K290
956 : !> \param k290_op K290 operation identifier
957 : !> \param valid whether a matching K290 operation was found
958 : ! **************************************************************************************************
959 156 : SUBROUTINE find_k290_kpoint_operation(csym, xref, xtarget, a1, a2, a3, b1, b2, b3, alat, &
960 : k290_op, valid)
961 : TYPE(csym_type) :: csym
962 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: xref, xtarget, a1, a2, a3, b1, b2, b3
963 : REAL(KIND=dp), INTENT(IN) :: alat
964 : INTEGER, INTENT(OUT) :: k290_op
965 : LOGICAL, INTENT(OUT) :: valid
966 :
967 : INTEGER :: ibsign, iop, kr
968 : REAL(KIND=dp) :: eps
969 : REAL(KIND=dp), DIMENSION(3) :: diff, rr, wcart
970 :
971 156 : eps = MAX(1.e-12_dp, 10.0_dp*csym%delta)
972 156 : k290_op = 0
973 156 : valid = .FALSE.
974 :
975 348 : DO iop = 1, csym%nrtot
976 348 : IF (iop > SIZE(csym%rt, 3)) CYCLE
977 348 : IF (csym%ibrot(iop) == 0) CYCLE
978 828 : DO ibsign = 1, 2
979 2544 : wcart(1:3) = alat*(xref(1)*b1(1:3) + xref(2)*b2(1:3) + xref(3)*b3(1:3))
980 2544 : wcart(1:3) = kp_apply_operation(wcart(1:3), csym%rt(1:3, 1:3, iop))
981 636 : IF (ibsign == 2) THEN
982 1152 : wcart(1:3) = -wcart(1:3)
983 288 : kr = -csym%ibrot(iop)
984 : ELSE
985 348 : kr = csym%ibrot(iop)
986 : END IF
987 2544 : rr(1) = DOT_PRODUCT(a1(1:3), wcart(1:3))/alat
988 2544 : rr(2) = DOT_PRODUCT(a2(1:3), wcart(1:3))/alat
989 2544 : rr(3) = DOT_PRODUCT(a3(1:3), wcart(1:3))/alat
990 :
991 2544 : diff(1:3) = xtarget(1:3) - rr(1:3)
992 2544 : diff(1:3) = diff(1:3) - ANINT(diff(1:3))
993 1504 : IF (ALL(ABS(diff(1:3)) < eps)) THEN
994 156 : k290_op = kr
995 156 : valid = .TRUE.
996 156 : RETURN
997 : END IF
998 : END DO
999 : END DO
1000 :
1001 : END SUBROUTINE find_k290_kpoint_operation
1002 :
1003 : ! **************************************************************************************************
1004 : !> \brief Score SPGLIB operations to choose stable atom transformations
1005 : !> \param csym ...
1006 : !> \param iop operation index
1007 : !> \param srot integer rotation in fractional coordinates
1008 : !> \return score, lower values are preferred
1009 : ! **************************************************************************************************
1010 9618 : FUNCTION spglib_operation_score(csym, iop, srot) RESULT(score)
1011 : TYPE(csym_type), INTENT(IN) :: csym
1012 : INTEGER, INTENT(IN) :: iop
1013 : INTEGER, DIMENSION(3, 3), INTENT(IN) :: srot
1014 : INTEGER :: score
1015 :
1016 : INTEGER :: i, nat
1017 : INTEGER, DIMENSION(3, 3) :: eye, r2
1018 : REAL(KIND=dp) :: eps
1019 :
1020 9618 : eps = MAX(1.e-12_dp, 10.0_dp*csym%delta)
1021 9618 : nat = SIZE(csym%f0, 1)
1022 9618 : score = 0
1023 86742 : DO i = 1, nat
1024 86742 : IF (csym%f0(i, iop) /= i) score = score + 100
1025 : END DO
1026 14472 : IF (ANY(ABS(csym%vt(1:3, iop) - ANINT(csym%vt(1:3, iop))) > eps)) score = score + 10
1027 :
1028 9618 : eye = 0
1029 9618 : eye(1, 1) = 1
1030 9618 : eye(2, 2) = 1
1031 9618 : eye(3, 3) = 1
1032 384720 : r2(1:3, 1:3) = MATMUL(srot(1:3, 1:3), srot(1:3, 1:3))
1033 61834 : IF (ANY(r2(1:3, 1:3) /= eye(1:3, 1:3))) score = score + 1
1034 :
1035 9618 : END FUNCTION spglib_operation_score
1036 :
1037 : ! **************************************************************************************************
1038 : !> \brief Reciprocal-space rotation corresponding to a fractional direct-space rotation
1039 : !> \param rot direct-space rotation
1040 : !> \return reciprocal-space rotation
1041 : ! **************************************************************************************************
1042 9938 : FUNCTION reciprocal_rotation(rot) RESULT(krot)
1043 : INTEGER, DIMENSION(3, 3), INTENT(IN) :: rot
1044 : INTEGER, DIMENSION(3, 3) :: krot
1045 :
1046 : REAL(KIND=dp), DIMENSION(3, 3) :: rinv
1047 :
1048 129194 : rinv = inv_3x3(REAL(rot(1:3, 1:3), KIND=dp))
1049 129194 : krot(1:3, 1:3) = NINT(TRANSPOSE(rinv(1:3, 1:3)))
1050 :
1051 9938 : END FUNCTION reciprocal_rotation
1052 :
1053 : ! **************************************************************************************************
1054 : !> \brief Reduce a CP2K Monkhorst-Pack mesh using K290 symmetry operations
1055 : !> \param csym ...
1056 : !> \param xkp full k-point mesh in reciprocal lattice coordinates
1057 : !> \param wkp reduced k-point weights
1058 : !> \param kpop symmetry operation mapping the representative k-point to a mesh point
1059 : !> \param nc number of point group operations
1060 : !> \param ib K290 operation identifiers
1061 : !> \param r K290 rotation matrices
1062 : !> \param a1 first lattice vector
1063 : !> \param a2 second lattice vector
1064 : !> \param a3 third lattice vector
1065 : !> \param b1 first reciprocal lattice vector
1066 : !> \param b2 second reciprocal lattice vector
1067 : !> \param b3 third reciprocal lattice vector
1068 : !> \param alat lattice scaling used by K290
1069 : ! **************************************************************************************************
1070 62 : SUBROUTINE reduce_kpoint_mesh(csym, xkp, wkp, kpop, nc, ib, r, a1, a2, a3, b1, b2, b3, alat)
1071 : TYPE(csym_type) :: csym
1072 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: xkp
1073 : REAL(KIND=dp), DIMENSION(:) :: wkp
1074 : INTEGER, DIMENSION(:) :: kpop
1075 : INTEGER, INTENT(IN) :: nc
1076 : INTEGER, DIMENSION(48), INTENT(IN) :: ib
1077 : REAL(KIND=dp), DIMENSION(3, 3, 48), INTENT(IN) :: r
1078 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: a1, a2, a3, b1, b2, b3
1079 : REAL(KIND=dp), INTENT(IN) :: alat
1080 :
1081 : INTEGER :: i, ibsign, iop, j, kr, nkpts
1082 : REAL(KIND=dp) :: eps
1083 : REAL(KIND=dp), DIMENSION(3) :: diff, rr, wcart
1084 :
1085 62 : nkpts = SIZE(wkp)
1086 62 : eps = MAX(1.e-12_dp, 10.0_dp*csym%delta)
1087 :
1088 1782 : wkp = 0.0_dp
1089 1782 : kpop = 0
1090 1782 : csym%kplink(1, :) = 0
1091 :
1092 1782 : DO i = 1, nkpts
1093 1720 : IF (csym%kplink(1, i) /= 0) CYCLE
1094 :
1095 196 : csym%kplink(1, i) = i
1096 196 : wkp(i) = 1.0_dp
1097 196 : kpop(i) = 1
1098 :
1099 4702 : DO iop = 1, nc
1100 15052 : DO ibsign = 1, 2
1101 8888 : kr = ib(iop)
1102 35552 : wcart(1:3) = alat*(xkp(1, i)*b1(1:3) + xkp(2, i)*b2(1:3) + xkp(3, i)*b3(1:3))
1103 35552 : wcart(1:3) = kp_apply_operation(wcart(1:3), r(1:3, 1:3, kr))
1104 8888 : IF (ibsign == 2) THEN
1105 17776 : wcart(1:3) = -wcart(1:3)
1106 4444 : kr = -kr
1107 : END IF
1108 35552 : rr(1) = DOT_PRODUCT(a1(1:3), wcart(1:3))/alat
1109 35552 : rr(2) = DOT_PRODUCT(a2(1:3), wcart(1:3))/alat
1110 35552 : rr(3) = DOT_PRODUCT(a3(1:3), wcart(1:3))/alat
1111 :
1112 244268 : DO j = 1, nkpts
1113 977072 : diff(1:3) = xkp(1:3, j) - rr(1:3)
1114 977072 : diff(1:3) = diff(1:3) - ANINT(diff(1:3))
1115 339772 : IF (ALL(ABS(diff(1:3)) < eps)) THEN
1116 8888 : IF (csym%kplink(1, j) == 0) THEN
1117 1524 : csym%kplink(1, j) = i
1118 1524 : wkp(i) = wkp(i) + 1.0_dp
1119 1524 : kpop(j) = kr
1120 : ELSE
1121 7364 : CPASSERT(csym%kplink(1, j) == i)
1122 : END IF
1123 : EXIT
1124 : END IF
1125 : END DO
1126 : ! Some point-group operations are incompatible with the requested Monkhorst-Pack mesh.
1127 4444 : IF (j > nkpts) CYCLE
1128 : END DO
1129 : END DO
1130 : END DO
1131 :
1132 1782 : DO i = 1, nkpts
1133 1720 : CPASSERT(csym%kplink(1, i) /= 0)
1134 1782 : CPASSERT(kpop(i) /= 0)
1135 : END DO
1136 :
1137 62 : END SUBROUTINE reduce_kpoint_mesh
1138 :
1139 : !> \brief ...
1140 : !> \param nk ...
1141 : !> \param xkp ...
1142 : !> \param wkp ...
1143 : !> \param shift ...
1144 : ! **************************************************************************************************
1145 1068 : SUBROUTINE full_grid_gen(nk, xkp, wkp, shift)
1146 : INTEGER, INTENT(IN) :: nk(3)
1147 : REAL(KIND=dp), DIMENSION(:, :) :: xkp
1148 : REAL(KIND=dp), DIMENSION(:) :: wkp
1149 : REAL(KIND=dp), INTENT(IN) :: shift(3)
1150 :
1151 : INTEGER :: i, ix, iy, iz
1152 : REAL(KIND=dp) :: kpt_latt(3)
1153 :
1154 14812 : wkp = 0.0_dp
1155 1068 : i = 0
1156 3412 : DO ix = 1, nk(1)
1157 8802 : DO iy = 1, nk(2)
1158 21478 : DO iz = 1, nk(3)
1159 13744 : i = i + 1
1160 13744 : kpt_latt(1) = REAL(2*ix - nk(1) - 1, KIND=dp)/(2._dp*REAL(nk(1), KIND=dp))
1161 13744 : kpt_latt(2) = REAL(2*iy - nk(2) - 1, KIND=dp)/(2._dp*REAL(nk(2), KIND=dp))
1162 13744 : kpt_latt(3) = REAL(2*iz - nk(3) - 1, KIND=dp)/(2._dp*REAL(nk(3), KIND=dp))
1163 54976 : xkp(1:3, i) = kpt_latt(1:3)
1164 19134 : wkp(i) = 1.0_dp
1165 : END DO
1166 : END DO
1167 : END DO
1168 14812 : DO i = 1, nk(1)*nk(2)*nk(3)
1169 56044 : xkp(1:3, i) = xkp(1:3, i) + shift(1:3)
1170 : END DO
1171 :
1172 1068 : END SUBROUTINE full_grid_gen
1173 :
1174 : ! **************************************************************************************************
1175 : !> \brief ...
1176 : !> \param xkp ...
1177 : !> \param wkp ...
1178 : !> \param link ...
1179 : ! **************************************************************************************************
1180 948 : SUBROUTINE inversion_symm(xkp, wkp, link)
1181 : REAL(KIND=dp), DIMENSION(:, :) :: xkp
1182 : REAL(KIND=dp), DIMENSION(:) :: wkp
1183 : INTEGER, DIMENSION(:) :: link
1184 :
1185 : INTEGER :: i, j, nkpts
1186 : REAL(KIND=dp), DIMENSION(3) :: diff
1187 :
1188 948 : nkpts = SIZE(wkp, 1)
1189 :
1190 11794 : link(:) = 0
1191 11794 : DO i = 1, nkpts
1192 10846 : IF (link(i) == 0) link(i) = i
1193 160848 : DO j = i + 1, nkpts
1194 154458 : IF (wkp(j) == 0) CYCLE
1195 418728 : diff(1:3) = xkp(1:3, i) + xkp(1:3, j)
1196 418728 : diff(1:3) = diff(1:3) - ANINT(diff(1:3))
1197 153412 : IF (ALL(ABS(diff(1:3)) < 1.e-12_dp)) THEN
1198 5404 : wkp(i) = wkp(i) + wkp(j)
1199 5404 : wkp(j) = 0.0_dp
1200 5404 : link(j) = i
1201 5404 : EXIT
1202 : END IF
1203 : END DO
1204 : END DO
1205 :
1206 948 : END SUBROUTINE inversion_symm
1207 :
1208 : ! **************************************************************************************************
1209 : !> \brief ...
1210 : !> \param x ...
1211 : !> \param r ...
1212 : !> \return ...
1213 : ! **************************************************************************************************
1214 9524 : FUNCTION kp_apply_operation(x, r) RESULT(y)
1215 : REAL(KIND=dp), INTENT(IN) :: x(3), r(3, 3)
1216 : REAL(KIND=dp) :: y(3)
1217 :
1218 9524 : y(1) = r(1, 1)*x(1) + r(1, 2)*x(2) + r(1, 3)*x(3)
1219 9524 : y(2) = r(2, 1)*x(1) + r(2, 2)*x(2) + r(2, 3)*x(3)
1220 9524 : y(3) = r(3, 1)*x(1) + r(3, 2)*x(2) + r(3, 3)*x(3)
1221 :
1222 9524 : END FUNCTION kp_apply_operation
1223 :
1224 : ! **************************************************************************************************
1225 : !> \brief ...
1226 : !> \param csym ...
1227 : ! **************************************************************************************************
1228 1245 : SUBROUTINE print_crys_symmetry(csym)
1229 : TYPE(csym_type) :: csym
1230 :
1231 : INTEGER :: i, iunit, j, plevel
1232 :
1233 1245 : iunit = csym%punit
1234 1245 : IF (iunit >= 0) THEN
1235 616 : plevel = csym%plevel
1236 616 : WRITE (iunit, "(/,T2,A)") "Crystal Symmetry Information"
1237 616 : IF (csym%symlib) THEN
1238 615 : WRITE (iunit, '(A,T71,A10)') " International Symbol: ", ADJUSTR(TRIM(csym%international_symbol))
1239 615 : WRITE (iunit, '(A,T71,A10)') " Point Group Symbol: ", ADJUSTR(TRIM(csym%pointgroup_symbol))
1240 615 : WRITE (iunit, '(A,T71,A10)') " Schoenflies Symbol: ", ADJUSTR(TRIM(csym%schoenflies))
1241 : !
1242 615 : WRITE (iunit, '(A,T71,I10)') " Number of Symmetry Operations: ", csym%n_operations
1243 615 : IF (plevel > 0) THEN
1244 0 : DO i = 1, csym%n_operations
1245 : WRITE (iunit, '(A,i4,T51,3I10,/,T51,3I10,/,T51,3I10)') &
1246 0 : " Rotation #: ", i, (csym%rotations(j, :, i), j=1, 3)
1247 0 : WRITE (iunit, '(T36,3F15.7)') csym%translations(:, i)
1248 : END DO
1249 : END IF
1250 : ELSE
1251 1 : WRITE (iunit, "(T2,A)") "SPGLIB for Crystal Symmetry Information determination is not availale"
1252 : END IF
1253 : END IF
1254 :
1255 1245 : END SUBROUTINE print_crys_symmetry
1256 :
1257 : ! **************************************************************************************************
1258 : !> \brief ...
1259 : !> \param csym ...
1260 : ! **************************************************************************************************
1261 950 : SUBROUTINE print_kp_symmetry(csym)
1262 : TYPE(csym_type), INTENT(IN) :: csym
1263 :
1264 : INTEGER :: i, iunit, nat, plevel
1265 :
1266 950 : iunit = csym%punit
1267 950 : IF (iunit >= 0) THEN
1268 321 : plevel = csym%plevel
1269 321 : WRITE (iunit, "(/,T2,A)") "K-point Symmetry Information"
1270 321 : WRITE (iunit, '(A,T67,I14)') " Number of Special K-points: ", csym%nkpoint
1271 321 : WRITE (iunit, '(T19,A,T74,A)') " Wavevector Basis ", " Weight"
1272 2160 : DO i = 1, csym%nkpoint
1273 2160 : WRITE (iunit, '(T2,i10,3F10.5,T71,I10)') i, csym%xkpoint(1:3, i), NINT(csym%wkpoint(i))
1274 : END DO
1275 321 : WRITE (iunit, '(/,A,T63,3I6)') " K-point Mesh: ", csym%mesh(1), csym%mesh(2), csym%mesh(3)
1276 321 : WRITE (iunit, '(T19,A,T54,A)') " Wavevector Basis ", " Special Points Rotation"
1277 4978 : DO i = 1, csym%mesh(1)*csym%mesh(2)*csym%mesh(3)
1278 4657 : WRITE (iunit, '(T2,i10,3F10.5,T45,3I12)') i, csym%kpmesh(1:3, i), &
1279 9635 : csym%kplink(1:2, i), csym%kpop(i)
1280 : END DO
1281 321 : IF (csym%nrtot > 0) THEN
1282 35 : WRITE (iunit, '(/,A)') " Atom Transformation Table"
1283 35 : nat = SIZE(csym%f0, 1)
1284 2703 : DO i = 1, csym%nrtot
1285 2703 : WRITE (iunit, '(T10,A,I5,(T21,12I5))') " Rot=", csym%ibrot(i), csym%f0(1:nat, i)
1286 : END DO
1287 : END IF
1288 : END IF
1289 :
1290 950 : END SUBROUTINE print_kp_symmetry
1291 :
1292 0 : END MODULE cryssym
|