Octopus
symmetries.F90
Go to the documentation of this file.
1!! Copyright (C) 2009 X. Andrade
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
22 use debug_oct_m
23 use global_oct_m
24 use iso_c_binding
25 use, intrinsic :: iso_fortran_env
27 use math_oct_m
29 use mpi_oct_m
31 use parser_oct_m
33 use string_oct_m
34 use space_oct_m
35 use spglib_f08
37
38 implicit none
39
40 private
41
42 public :: &
53
54 type symmetries_t
55 private
56 type(symm_op_t), allocatable, public :: ops(:)
57 integer, public :: nops = 0
58 real(real64) :: breakdir(1:3)
59 integer, public :: periodic_dim
60 integer :: space_group
61 logical :: any_non_spherical
62 logical :: symmetries_compute = .false.
63 character(len=6) :: group_name = ""
64 character(len=30) :: group_elements = ""
65 character(len=11) :: symbol = ""
66 character(len=7) :: schoenflies = ""
67
68 type(symm_op_t), allocatable, public :: non_symmorphic_ops(:)
69 integer, public :: nops_nonsymmorphic = 0
70 logical :: use_nonsymmorphic = .true.
71 contains
72 procedure :: copy => symmetries_copy
73 generic :: assignment(=) => copy
75 end type symmetries_t
76
77 real(real64), parameter, public :: default_symprec = 1.e-5_real64
78 real(real64), public :: symprec
79
81 interface
82 subroutine symmetries_finite_init(natoms, types, positions, verbosity, point_group)
83 use, intrinsic :: iso_fortran_env
84 integer, intent(in) :: natoms
85 integer, intent(in) :: types
86 real(real64), intent(in) :: positions
87 integer, intent(in) :: verbosity
88 integer, intent(out) :: point_group
89 end subroutine symmetries_finite_init
90
91 subroutine symmetries_finite_get_group_name(point_group, name) bind(c)
92 use iso_c_binding
93 integer, intent(in) :: point_group
94 character(kind=c_char), intent(out) :: name(*)
96
97 subroutine symmetries_finite_get_group_elements(point_group, elements) bind(c)
98 use iso_c_binding
99 integer, intent(in) :: point_group
100 character(kind=c_char), intent(out) :: elements(*)
102
103 subroutine symmetries_finite_end()
104 end subroutine symmetries_finite_end
105 end interface
106
107 interface symmetries_t
108 module procedure symmetries_constructor
109 end interface symmetries_t
110
111contains
112
113 function symmetries_constructor(namespace, space, latt, n_sites, site_pos, site_type, spherical_site) result(this)
114 type(namespace_t), intent(in) :: namespace
115 class(space_t), intent(in) :: space
116 type(lattice_vectors_t), intent(in) :: latt
117 integer, intent(in) :: n_sites
118 real(real64), intent(in) :: site_pos(:,:)
119 integer, intent(in) :: site_type(1:n_sites)
120 logical, intent(in) :: spherical_site(:)
121 type(symmetries_t) :: this
122
123 integer :: dim4syms
124 integer :: idir, isite, isite_symm, iop, verbosity, point_group
125 real(real64), allocatable :: position(:, :)
126 type(block_t) :: blk
127 type(symm_op_t) :: tmpop
128 integer :: identity(3,3)
129 logical :: found_identity, is_supercell
130 logical :: def_sym_comp
131 real(real64) :: rsite(space%dim)
132 character(kind=c_char) :: c_str(64)
133
134 type(SpglibDataset) :: spg_dataset
135 type(SpglibSpacegroupType) :: spg_spacegroup
136
137 push_sub(symmetries_constructor)
138
139 ! if someone cares, they could try to analyze the symmetry point group of the individual species too
140 this%any_non_spherical = any(.not. spherical_site)
141
142 this%periodic_dim = space%periodic_dim
143
144 dim4syms = min(3, space%dim)
145
146 def_sym_comp = (n_sites < 100) .or. space%is_periodic()
147 def_sym_comp = def_sym_comp .and. space%dim == 3
148
149 !%Variable SymmetriesCompute
150 !%Type logical
151 !%Section Execution::Symmetries
152 !%Description
153 !% If disabled, <tt>Octopus</tt> will not compute
154 !% nor print the symmetries.
155 !%
156 !% By default, symmetries are computed when running in 3
157 !% dimensions for systems with less than 100 atoms.
158 !% For periodic systems, the default is always true, irrespective of the number of atoms.
159 !%End
160 call parse_variable(namespace, 'SymmetriesCompute', def_sym_comp, this%symmetries_compute)
162 !%Variable SymmetriesTolerance
163 !%Type float
164 !%Section Execution::Symmetries
165 !%Description
166 !% For periodic systems, this variable controls the tolerance used by the symmetry finder
167 !% (spglib) to find the spacegroup and symmetries of the crystal.
168 !%End
169 call parse_variable(namespace, 'SymmetriesTolerance', default_symprec, symprec)
170
171
172 if (this%symmetries_compute .and. space%dim /= 3) then
173 call messages_experimental('symmetries for non 3D systems', namespace=namespace)
174 end if
175
176 if (this%any_non_spherical .or. .not. this%symmetries_compute) then
179 return
180 end if
181
182 ! In all cases, we must check that the grid respects the symmetries. --DAS
183
184 if (space%periodic_dim == 0) then
185
187
188 ! for the moment symmetries are only used for information, so we compute them only on one node.
189 if (mpi_world%is_root()) then
190 verbosity = -1
191
192 safe_allocate(position(1:3, 1:n_sites))
193
194 do isite = 1, n_sites
195 position(1:3, isite) = m_zero
196 position(1:dim4syms, isite) = site_pos(1:dim4syms, isite)
197 end do
199 if (this%symmetries_compute) then
200 call symmetries_finite_init(n_sites, site_type(1), position(1, 1), verbosity, point_group)
201 if (point_group > -1) then
202 c_str = c_null_char
203 call symmetries_finite_get_group_name(point_group, c_str)
204 call string_c_to_f(c_str, this%group_name)
205
206 c_str = c_null_char
207 call symmetries_finite_get_group_elements(point_group, c_str)
208 call string_c_to_f(c_str, this%group_elements)
209 else
210 this%group_name = ""
211 this%group_elements = ""
212 this%symbol = ""
213 this%schoenflies = ""
214 this%symmetries_compute = .false.
215 end if
217 end if
218 safe_deallocate_a(position)
219 end if
220
221 else
222
223 !%Variable SymmetriesUseNonSymmorphic
224 !%Type logical
225 !%Default yes
226 !%Section Execution::Symmetries
227 !%Description
228 !% For periodic systems, this variable controls if the code tries to use available
229 !% non-symmorphic symmetries.
230 !%End
231 call parse_variable(namespace, 'SymmetriesUseNonSymmorphic', .true., this%use_nonsymmorphic)
232
233
234 spg_dataset = symmetries_get_spg_dataset(namespace, space, latt, n_sites, site_pos, site_type)
235 if (spg_dataset%spglib_error /= 0) then
236 call init_identity()
238 return
239 end if
240
241 this%space_group = spg_dataset%spacegroup_number
242 this%symbol = spg_dataset%international_symbol
243 spg_spacegroup = spg_get_spacegroup_type(spg_dataset%hall_number)
244 this%schoenflies = spg_spacegroup%schoenflies
245
246 do iop = 1, spg_dataset%n_operations
247 ! transpose due to array order difference between C and fortran
248 spg_dataset%rotations(:,:,iop) = transpose(spg_dataset%rotations(:,:,iop))
249 ! sometimes spglib may return lattice vectors as 'fractional' translations
250 spg_dataset%translations(:, iop) = spg_dataset%translations(:, iop) - &
251 anint(spg_dataset%translations(:, iop) + m_half * tol_translation)
252 end do
253
254 ! we need to check that it is not a supercell, as in the QE routine (sgam_at)
255 ! they disable fractional translations if the identity has one, because the sym ops might not form a group.
256 ! spglib may return duplicate operations in this case!
257 is_supercell = (spg_dataset%n_operations > 48)
258 found_identity = .false.
259 identity = diagonal_matrix(3, 1)
260 do iop = 1, spg_dataset%n_operations
261 if (all(spg_dataset%rotations(1:3, 1:3, iop) == identity(1:3, 1:3))) then
262 found_identity = .true.
263 if (any(abs(spg_dataset%translations(1:3, iop)) > tol_translation )) then
264 is_supercell = .true.
265 write(message(1),'(a,3f12.6)') 'Identity has a fractional translation ', spg_dataset%translations(1:3, iop)
266 call messages_info(1, namespace=namespace)
267 end if
268 end if
269 end do
270 if (.not. found_identity) then
271 message(1) = "Symmetries internal error: Identity is missing from symmetry operations."
272 call messages_fatal(1, namespace=namespace)
273 end if
274
275 if (is_supercell) then
276 message(1) = "Disabling fractional translations. System appears to be a supercell."
277 call messages_info(1, namespace=namespace)
278 end if
279 ! actually, we do not use fractional translations regardless currently
280
281 ! this is a hack to get things working, this variable should be
282 ! eliminated and the direction calculated automatically from the
283 ! perturbations.
284
285 !%Variable SymmetryBreakDir
286 !%Type block
287 !%Section Mesh::Simulation Box
288 !%Description
289 !% This variable specifies a direction in which the symmetry of
290 !% the system will be broken. This is useful for generating <i>k</i>-point
291 !% grids when an external perturbation is applied.
292 !%End
293
294 this%breakdir(1:3) = m_zero
295
296 if (parse_block(namespace, 'SymmetryBreakDir', blk) == 0) then
297
298 do idir = 1, dim4syms
299 call parse_block_float(blk, 0, idir - 1, this%breakdir(idir))
300 end do
301
302 call parse_block_end(blk)
303
304 end if
305
306 safe_allocate(this%ops(1:spg_dataset%n_operations))
307 safe_allocate(this%non_symmorphic_ops(1:spg_dataset%n_operations))
308
309 ! check all operations and leave those that kept the symmetry-breaking
310 ! direction invariant and (for the moment) that do not have a translation
311 this%nops = 0
312 ! We also keep track of all non-symmorphic operations
313 this%nops_nonsymmorphic = 0
314 do iop = 1, spg_dataset%n_operations
315 ! If spglib returns tiny translations, we set them to zero to avoid numerical artifacts
316 if (all(abs(spg_dataset%translations(1:3, iop)) < tol_translation)) then
317 spg_dataset%translations(1:3, iop) = m_zero
318 end if
319
320 ! Reject all symmetries involving fractional translation in the non-periodic dimension
321 if (any(abs(spg_dataset%translations(space%periodic_dim+1:3, iop)) > tol_translation)) cycle
322
323 call symm_op_init(tmpop, spg_dataset%rotations(1:3, 1:3, iop), latt, dim4syms, spg_dataset%translations(1:3, iop))
324
325 ! If we do not specify SymmetryBreakDir, we want to use all symmetries
326 if( all(abs(this%breakdir) < m_epsilon) ) then
327 if (.not. symm_op_has_translation(tmpop, tol_translation)) then
328 this%nops = this%nops + 1
329 call symm_op_copy(tmpop, this%ops(this%nops))
330 else if(.not. is_supercell .and. this%use_nonsymmorphic) then
331 this%nops_nonsymmorphic = this%nops_nonsymmorphic + 1
332 call symm_op_copy(tmpop, this%non_symmorphic_ops(this%nops_nonsymmorphic))
333 end if
334 else if (symm_op_invariant_cart(tmpop, this%breakdir, symprec)) then
335 if(.not. symm_op_has_translation(tmpop, symprec)) then
336 this%nops = this%nops + 1
337 call symm_op_copy(tmpop, this%ops(this%nops))
338 else if(.not. is_supercell .and. this%use_nonsymmorphic) then
339 this%nops_nonsymmorphic = this%nops_nonsymmorphic + 1
340 call symm_op_copy(tmpop, this%non_symmorphic_ops(this%nops_nonsymmorphic))
341 end if
342 end if
343 end do
344
345
346 end if
347
348 call symmetries_write_info(this, space, namespace=namespace)
349
350 ! Checks if that the atomic coordinates are compatible with the symmetries
351 !
352 ! We want to use for instance that
353 !
354 ! \int dr f(Rr) V_isite(r) \nabla f(R(v)) = R\int dr f(r) V_isite(R*r) f(r)
355 !
356 ! and that the operator R should map the position of atom
357 ! isite to the position of some other atom isite_symm, so that
358 !
359 ! V_isite(R*r) = V_isite_symm(r)
360 !
361 do iop = 1, symmetries_number(this)
362 if (iop == symmetries_identity_index(this)) cycle
363
364 do isite = 1, n_sites
365 rsite = symm_op_apply_cart(this%ops(iop), site_pos(:, isite))
366
367 rsite = latt%fold_into_cell(rsite)
368
369 ! find isite_symm
370 do isite_symm = 1, n_sites
371 if (all(abs(rsite - site_pos(:, isite_symm)) < symprec)) exit
372 end do
373
374 if (isite_symm > n_sites) then
375 write(message(1),'(a,i6)') 'Internal error: could not find symmetric partner for atom number', isite
376 write(message(2),'(a,i3,a)') 'with symmetry operation number ', iop, '.'
377 call messages_fatal(2, namespace=namespace)
378 end if
379
380 end do
381 end do
382
383 ! Check non-symmorphic operations
384 do iop = 1, this%nops_nonsymmorphic
385 do isite = 1, n_sites
386 rsite = symm_op_apply_cart(this%non_symmorphic_ops(iop), site_pos(:, isite))
387
388 rsite = latt%fold_into_cell(rsite)
389
390 ! find isite_symm
391 do isite_symm = 1, n_sites
392 if (all(abs(rsite - site_pos(:, isite_symm)) < symprec)) exit
393 end do
394
395 if (isite_symm > n_sites) then
396 write(message(1),'(a,i6)') 'Internal error: could not find symmetric partner for atom number', isite
397 write(message(2),'(a,i3,a)') 'with non-symmorphic symmetry operation number ', iop, '.'
398 call messages_fatal(2, namespace=namespace)
399 end if
400
401 end do
402 end do
403
405
406 contains
407
408 subroutine init_identity()
409
410 push_sub(symmetries_init.init_identity)
411
412 safe_allocate(this%ops(1))
413 this%nops = 1
414 call symm_op_init(this%ops(1), diagonal_matrix(3, 1), latt, dim4syms)
415 this%nops_nonsymmorphic = 0
416 this%breakdir = m_zero
417 this%space_group = 1
418
419 pop_sub(symmetries_init.init_identity)
420
421 end subroutine init_identity
422
423 end function symmetries_constructor
424
425 ! -------------------------------------------------------------------------------
426 subroutine symmetries_copy(lhs, rhs)
427 class(symmetries_t), intent(out) :: lhs
428 class(symmetries_t), intent(in) :: rhs
429
430 integer :: iop
431
432 push_sub(symmetries_copy)
433
434 safe_allocate(lhs%ops(1:ubound(rhs%ops, dim=1)))
435 do iop = 1, rhs%nops
436 call symm_op_copy(rhs%ops(iop), lhs%ops(iop))
437 end do
438 lhs%nops = rhs%nops
439 safe_allocate(lhs%non_symmorphic_ops(1:rhs%nops_nonsymmorphic))
440 do iop = 1, rhs%nops_nonsymmorphic
441 call symm_op_copy(rhs%non_symmorphic_ops(iop), lhs%non_symmorphic_ops(iop))
442 end do
443 lhs%nops_nonsymmorphic = rhs%nops_nonsymmorphic
444 lhs%breakdir = rhs%breakdir
445 lhs%periodic_dim = rhs%periodic_dim
446 lhs%space_group = rhs%space_group
447 lhs%any_non_spherical = rhs%any_non_spherical
448 lhs%symmetries_compute = rhs%symmetries_compute
449 lhs%group_name = rhs%group_name
450 lhs%group_elements = rhs%group_elements
451 lhs%symbol = rhs%symbol
452 lhs%schoenflies = rhs%schoenflies
453 lhs%use_nonsymmorphic = rhs%use_nonsymmorphic
454
455 pop_sub(symmetries_copy)
456 end subroutine symmetries_copy
457
458 ! -------------------------------------------------------------------------------
459
460 subroutine symmetries_finalizer(this)
461 type(symmetries_t), intent(inout) :: this
462
463 push_sub(symmetries_finalizer)
464
465 safe_deallocate_a(this%ops)
466 safe_deallocate_a(this%non_symmorphic_ops)
467
468 pop_sub(symmetries_finalizer)
469 end subroutine symmetries_finalizer
470
471 ! -------------------------------------------------------------------------------
472
473 integer pure function symmetries_number(this) result(number)
474 type(symmetries_t), intent(in) :: this
475
476 number = this%nops
477 end function symmetries_number
478
479 ! -------------------------------------------------------------------------------
480
481 subroutine symmetries_apply_kpoint_red(this, iop, aa, bb)
482 type(symmetries_t), intent(in) :: this
483 integer, intent(in) :: iop
484 real(real64), intent(in) :: aa(1:3)
485 real(real64), intent(out) :: bb(1:3)
486
488
489 assert(iop /= 0 .and. abs(iop) <= this%nops)
490
491 !Time-reversal symmetry
492 if (iop < 0) then
493 bb(1:3) = symm_op_apply_transpose_red(this%ops(abs(iop)), -aa(1:3))
494 else
495 bb(1:3) = symm_op_apply_transpose_red(this%ops(abs(iop)), aa(1:3))
496 end if
497
499 end subroutine symmetries_apply_kpoint_red
500
501 ! -------------------------------------------------------------------------------
502
503 integer pure function symmetries_space_group_number(this) result(number)
504 type(symmetries_t), intent(in) :: this
505
506 number = this%space_group
508
509 ! -------------------------------------------------------------------------------
510
511 logical pure function symmetries_have_break_dir(this) result(have)
512 type(symmetries_t), intent(in) :: this
513
514 have = any(abs(this%breakdir(1:3)) > m_epsilon)
515 end function symmetries_have_break_dir
516
517 ! -------------------------------------------------------------------------------
518
519 integer pure function symmetries_identity_index(this) result(index)
520 type(symmetries_t), intent(in) :: this
522 integer :: iop
523
524 index = -1
525 do iop = 1, this%nops
526 if (symm_op_is_identity(this%ops(iop))) then
527 index = iop
528 cycle
529 end if
530 end do
531
532 end function symmetries_identity_index
533
534 ! ---------------------------------------------------------
535 subroutine symmetries_write_info(this, space, iunit, namespace)
536 type(symmetries_t), intent(in) :: this
537 class(space_t), intent(in) :: space
538 integer, optional, intent(in) :: iunit
539 type(namespace_t), optional, intent(in) :: namespace
540
541 integer :: iop
542
543 push_sub(symmetries_write_info)
544
545 call messages_print_with_emphasis(msg='Symmetries', iunit=iunit, namespace=namespace)
546
547 if (this%any_non_spherical) then
548 message(1) = "Symmetries are disabled since non-spherically symmetric species may be present."
549 call messages_info(1,iunit = iunit, namespace=namespace)
550 call messages_print_with_emphasis(iunit=iunit, namespace=namespace)
551 pop_sub(symmetries_write_info)
552 return
553 end if
554
555 if (.not. this%symmetries_compute) then
556 message(1) = "Symmetries have been disabled by SymmetriesCompute = false."
557 call messages_info(1, iunit=iunit, namespace=namespace)
558 call messages_print_with_emphasis(iunit=iunit, namespace=namespace)
559 pop_sub(symmetries_write_info)
560 return
561 end if
562
563 if (space%periodic_dim == 0) then
564 ! At the moment only the root node has information about symetries of finite systems.
565 if (mpi_world%is_root()) then
566 if (this%symmetries_compute) then
567 call messages_write('Symmetry elements : '//trim(this%group_elements), new_line = .true.)
568 call messages_write('Symmetry group : '//trim(this%group_name))
569 call messages_info(iunit=iunit, namespace=namespace)
570 end if
571 end if
572 else
573 write(message(1),'(a, i4)') 'Space group No. ', this%space_group
574 write(message(2),'(2a)') 'International: ', trim(this%symbol)
575 write(message(3),'(2a)') 'Schoenflies: ', trim(this%schoenflies)
576 call messages_info(3, iunit=iunit, namespace=namespace)
577
578 write(message(1), '(a,i5,a)') 'Info: The system has ', this%nops, ' symmetries that can be used.'
579 call messages_info(1, iunit=iunit, namespace=namespace)
580
581 write(message(1),'(a7,a31,12x,a33)') 'Index', 'Rotation matrix', 'Fractional translations'
582 call messages_info(1, iunit=iunit, namespace=namespace)
583 do iop = 1, this%nops
584 ! list all operations and leave those that kept the symmetry-breaking
585 ! direction invariant and (for the moment) that do not have a translation
586 if (space%dim == 1) then
587 write(message(1),'(i5,1x,a,2x,1(1i4,2x),1f12.6)') iop, ':', symm_op_rotation_matrix_red(this%ops(iop)), &
588 symm_op_translation_vector_red(this%ops(iop))
589 end if
590 if (space%dim == 2) then
591 write(message(1),'(i5,1x,a,2x,2(2i4,2x),2f12.6)') iop, ':', symm_op_rotation_matrix_red(this%ops(iop)), &
592 symm_op_translation_vector_red(this%ops(iop))
593 end if
594 if (space%dim == 3) then
595 write(message(1),'(i5,1x,a,2x,3(3i4,2x),3f12.6)') iop, ':', symm_op_rotation_matrix_red(this%ops(iop)), &
596 symm_op_translation_vector_red(this%ops(iop))
597 end if
598 call messages_info(1, iunit=iunit, namespace=namespace)
599 end do
600
601 write(message(1), '(a,i5,a)') 'Info: The system also has ', this%nops_nonsymmorphic, &
602 ' nonsymmorphic symmetries that are not used for electrons.'
603 call messages_info(1, iunit=iunit, namespace=namespace)
604 do iop = 1, this%nops_nonsymmorphic
605 ! list all operations and leave those that kept the symmetry-breaking
606 ! direction invariant and (for the moment) that do not have a translation
607 if (space%dim == 1) then
608 write(message(1),'(i5,1x,a,2x,1(1i4,2x),1f12.6)') iop, ':', symm_op_rotation_matrix_red(this%non_symmorphic_ops(iop)), &
609 symm_op_translation_vector_red(this%non_symmorphic_ops(iop))
610 end if
611 if (space%dim == 2) then
612 write(message(1),'(i5,1x,a,2x,2(2i4,2x),2f12.6)') iop, ':', symm_op_rotation_matrix_red(this%non_symmorphic_ops(iop)), &
613 symm_op_translation_vector_red(this%non_symmorphic_ops(iop))
614 end if
615 if (space%dim == 3) then
616 write(message(1),'(i5,1x,a,2x,3(3i4,2x),3f12.6)') iop, ':', symm_op_rotation_matrix_red(this%non_symmorphic_ops(iop)), &
617 symm_op_translation_vector_red(this%non_symmorphic_ops(iop))
618 end if
619 call messages_info(1, iunit=iunit, namespace=namespace)
620 end do
621 end if
622 call messages_print_with_emphasis(iunit=iunit, namespace=namespace)
623
624 pop_sub(symmetries_write_info)
625 end subroutine symmetries_write_info
626
627 ! ---------------------------------------------------------
629 subroutine symmetries_update_lattice_vectors(this, latt, dim)
630 type(symmetries_t), intent(inout) :: this
631 type(lattice_vectors_t), intent(in) :: latt
632 integer, intent(in) :: dim
633
634 integer :: iop
635
637
638 do iop = 1, this%nops
639 call symm_op_build_cartesian(this%ops(iop), latt, dim)
640 end do
641 do iop = 1, this%nops_nonsymmorphic
642 call symm_op_build_cartesian(this%non_symmorphic_ops(iop), latt, dim)
643 end do
644
646 end subroutine
647
649 type(spglibdataset) function symmetries_get_spg_dataset(namespace, space, latt, n_sites, site_pos, site_type) result(spg_dataset)
650 type(namespace_t), intent(in) :: namespace
651 class(space_t), intent(in) :: space
652 type(lattice_vectors_t), intent(in) :: latt
653 integer, intent(in) :: n_sites
654 real(real64), intent(in) :: site_pos(:,:)
655 integer, intent(in) :: site_type(1:n_sites)
656
657 integer :: isite, idir, dim4syms
658 real(real64), allocatable :: position(:, :)
659 real(real64) :: lattice(1:3, 1:3)
660
662
663 assert(space%is_periodic())
664
665 dim4syms = min(3, space%dim)
666
667 safe_allocate(position(1:3, 1:n_sites))
668
669 do isite = 1, n_sites
670 position(1:3,isite) = m_zero
671
672 ! Transform atomic positions to reduced coordinates
673 position(1:dim4syms, isite) = latt%cart_to_red(site_pos(1:dim4syms, isite))
674 position(1:dim4syms, isite) = position(1:dim4syms, isite) - m_half
675 do idir = 1, dim4syms
676 position(idir, isite) = position(idir, isite) - anint(position(idir, isite))
677 end do
678 position(1:dim4syms, isite) = position(1:dim4syms,isite) + m_half
679 end do
680
681 lattice = m_zero
682 !NOTE: Why "inverse matrix" ? (NTD)
683 ! get inverse matrix to extract reduced coordinates for spglib
684 lattice(1:space%periodic_dim, 1:space%periodic_dim) = latt%rlattice(1:space%periodic_dim, 1:space%periodic_dim)
685 ! transpose the lattice vectors for use in spglib as row-major matrix
686 lattice(:,:) = transpose(lattice(:,:))
687 ! spglib always assume 3D periodicity, so we need to set the lattice vectors along the non-periodic dimensions.
688 ! Here we choose a value that should guarantee that no incorrect symmetries are found along the non-periodic dimensions.
689 do idir = space%periodic_dim + 1, 3
690 if (idir <= space%dim) then
691 lattice(idir, idir) = m_two*(maxval(site_pos(idir, :)) - minval(site_pos(idir, :))) + m_one
692 else
693 lattice(idir, idir) = m_one
694 end if
695 end do
696
697 spg_dataset = spg_get_dataset(lattice, position, site_type, n_sites, symprec)
698 if (spg_dataset%spglib_error /= 0) then
699 message(1) = "Symmetry analysis failed in spglib. Disabling symmetries."
700 call messages_warning(1, namespace=namespace)
701
702 do isite = 1, n_sites
703 write(message(1),'(a,i6,a,3f12.6,a,3f12.6)') 'type ', site_type(isite), &
704 ' reduced coords ', position(:, isite), ' cartesian coords ', site_pos(:, isite)
705 call messages_info(1, namespace=namespace)
706 end do
707 end if
708 safe_deallocate_a(position)
709
711 end function symmetries_get_spg_dataset
712
715 subroutine symmetries_use_compatible_nonsymmorphic(this, ll, namespace)
716 type(symmetries_t), intent(inout) :: this
717 integer, intent(in) :: ll(3)
718 type(namespace_t), intent(in) :: namespace
719
720 integer :: iop, iop2, ncompatible, nops_nonsymmorphic
721 real(real64) :: trans(3)
722
725
726 ncompatible = 0
727 nops_nonsymmorphic = this%nops_nonsymmorphic
728 iop = 1
729 do while (iop <= nops_nonsymmorphic)
730 trans = symm_op_translation_vector_red(this%non_symmorphic_ops(iop))
731
732 if (all(abs(trans * ll - anint(trans * ll)) < symprec) ) then
733 ncompatible = ncompatible + 1
734 call symm_op_copy(this%non_symmorphic_ops(iop), this%ops(this%nops + ncompatible))
735 do iop2 = iop + 1, nops_nonsymmorphic
736 call symm_op_copy(this%non_symmorphic_ops(iop2), this%non_symmorphic_ops(iop2-1))
737 end do
738 nops_nonsymmorphic = nops_nonsymmorphic - 1
739 else
740 iop = iop + 1
741 end if
742 end do
743
744 if (ncompatible == 0) then
746 return
747 end if
748
749 call messages_print_with_emphasis(msg='Symmetries refinement', namespace=namespace)
750
751 write(message(1), '(a,i5,a)') 'Info: The system has ', ncompatible, &
752 ' non-symmorphic symmetries that are compatible with the grid.'
753 call messages_info(1, namespace=namespace)
754
755 write(message(1),'(a7,a31,12x,a33)') 'Index', 'Rotation matrix', 'Fractional translations'
756 call messages_info(1, namespace=namespace)
757 do iop = this%nops+1, this%nops+ncompatible
758 write(message(1),'(i5,1x,a,2x,3(3i4,2x),3f12.6)') iop, ':', symm_op_rotation_matrix_red(this%ops(iop)), &
759 symm_op_translation_vector_red(this%ops(iop))
760 call messages_info(1, namespace=namespace)
761 end do
762
763 this%nops_nonsymmorphic = nops_nonsymmorphic
764 this%nops = this%nops + ncompatible
765
766 call messages_print_with_emphasis(namespace=namespace)
767
770
771end module symmetries_oct_m
772
773!! Local Variables:
774!! mode: f90
775!! coding: utf-8
776!! End:
NOTE: unfortunately, these routines use global variables shared among them.
Definition: symmetries.F90:177
real(real64), parameter, public m_zero
Definition: global.F90:200
real(real64), parameter, public m_epsilon
Definition: global.F90:216
real(real64), parameter, public m_half
Definition: global.F90:206
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
subroutine, public messages_experimental(name, namespace)
Definition: messages.F90:1040
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
type(mpi_grp_t), public mpi_world
Definition: mpi.F90:272
integer function, public parse_block(namespace, name, blk, check_varinfo_)
Definition: parser.F90:623
subroutine, public string_c_to_f(c_string, f_string)
convert a C string to a Fortran string
Definition: string.F90:276
subroutine, public symm_op_copy(inp, outp)
Definition: symm_op.F90:278
logical pure function, public symm_op_has_translation(this, prec)
Definition: symm_op.F90:295
real(real64), parameter, public tol_translation
Definition: symm_op.F90:185
subroutine, public symm_op_init(this, rot, latt, dim, trans)
Definition: symm_op.F90:192
subroutine, public symmetries_update_lattice_vectors(this, latt, dim)
Updates the symmetry operations when lattice vectors are updated.
Definition: symmetries.F90:725
subroutine, public symmetries_apply_kpoint_red(this, iop, aa, bb)
Definition: symmetries.F90:577
integer pure function, public symmetries_space_group_number(this)
Definition: symmetries.F90:599
integer pure function, public symmetries_identity_index(this)
Definition: symmetries.F90:615
type(symmetries_t) function symmetries_constructor(namespace, space, latt, n_sites, site_pos, site_type, spherical_site)
Definition: symmetries.F90:209
subroutine, public symmetries_write_info(this, space, iunit, namespace)
Definition: symmetries.F90:631
real(real64), public symprec
Definition: symmetries.F90:173
integer pure function, public symmetries_number(this)
Definition: symmetries.F90:569
subroutine symmetries_copy(lhs, rhs)
Definition: symmetries.F90:522
logical pure function, public symmetries_have_break_dir(this)
Definition: symmetries.F90:607
type(spglibdataset) function, public symmetries_get_spg_dataset(namespace, space, latt, n_sites, site_pos, site_type)
Returns the SpglibDataset for the system.
Definition: symmetries.F90:745
subroutine symmetries_finalizer(this)
Definition: symmetries.F90:556
subroutine, public symmetries_use_compatible_nonsymmorphic(this, ll, namespace)
Detect if some non-symmorphic operations are compatible with the real-space grid If yes,...
Definition: symmetries.F90:811
subroutine init_identity()
Definition: symmetries.F90:504
int true(void)