25 use,
intrinsic :: iso_fortran_env
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 =
""
68 type(symm_op_t),
allocatable,
public :: non_symmorphic_ops(:)
69 integer,
public :: nops_nonsymmorphic = 0
70 logical :: use_nonsymmorphic = .
true.
73 generic ::
assignment(=) => copy
77 real(real64),
parameter,
public :: default_symprec = 1.e-5_real64
78 real(real64),
public :: symprec
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
93 integer,
intent(in) :: point_group
94 character(kind=c_char),
intent(out) :: name(*)
99 integer,
intent(in) :: point_group
100 character(kind=c_char),
intent(out) :: elements(*)
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
124 integer :: idir, isite, isite_symm, iop, verbosity, point_group
125 real(real64),
allocatable :: position(:, :)
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)
134 type(SpglibDataset) :: spg_dataset
135 type(SpglibSpacegroupType) :: spg_spacegroup
140 this%any_non_spherical = any(.not. spherical_site)
142 this%periodic_dim = space%periodic_dim
144 dim4syms = min(3, space%dim)
146 def_sym_comp = (n_sites < 100) .or. space%is_periodic()
147 def_sym_comp = def_sym_comp .and. space%dim == 3
160 call parse_variable(namespace,
'SymmetriesCompute', def_sym_comp, this%symmetries_compute)
172 if (this%symmetries_compute .and. space%dim /= 3)
then
176 if (this%any_non_spherical .or. .not. this%symmetries_compute)
then
184 if (space%periodic_dim == 0)
then
192 safe_allocate(position(1:3, 1:n_sites))
194 do isite = 1, n_sites
195 position(1:3, isite) =
m_zero
196 position(1:dim4syms, isite) = site_pos(1:dim4syms, isite)
199 if (this%symmetries_compute)
then
201 if (point_group > -1)
then
211 this%group_elements =
""
213 this%schoenflies =
""
214 this%symmetries_compute = .false.
218 safe_deallocate_a(position)
231 call parse_variable(namespace,
'SymmetriesUseNonSymmorphic', .
true., this%use_nonsymmorphic)
235 if (spg_dataset%spglib_error /= 0)
then
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
246 do iop = 1, spg_dataset%n_operations
248 spg_dataset%rotations(:,:,iop) = transpose(spg_dataset%rotations(:,:,iop))
250 spg_dataset%translations(:, iop) = spg_dataset%translations(:, iop) - &
257 is_supercell = (spg_dataset%n_operations > 48)
258 found_identity = .false.
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)
270 if (.not. found_identity)
then
271 message(1) =
"Symmetries internal error: Identity is missing from symmetry operations."
275 if (is_supercell)
then
276 message(1) =
"Disabling fractional translations. System appears to be a supercell."
294 this%breakdir(1:3) =
m_zero
296 if (
parse_block(namespace,
'SymmetryBreakDir', blk) == 0)
then
298 do idir = 1, dim4syms
306 safe_allocate(this%ops(1:spg_dataset%n_operations))
307 safe_allocate(this%non_symmorphic_ops(1:spg_dataset%n_operations))
313 this%nops_nonsymmorphic = 0
314 do iop = 1, spg_dataset%n_operations
316 if (all(abs(spg_dataset%translations(1:3, iop)) <
tol_translation))
then
317 spg_dataset%translations(1:3, iop) =
m_zero
321 if (any(abs(spg_dataset%translations(space%periodic_dim+1:3, iop)) >
tol_translation)) cycle
323 call symm_op_init(tmpop, spg_dataset%rotations(1:3, 1:3, iop), latt, dim4syms, spg_dataset%translations(1:3, iop))
326 if( all(abs(this%breakdir) <
m_epsilon) )
then
328 this%nops = this%nops + 1
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))
336 this%nops = this%nops + 1
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))
364 do isite = 1, n_sites
367 rsite = latt%fold_into_cell(rsite)
370 do isite_symm = 1, n_sites
371 if (all(abs(rsite - site_pos(:, isite_symm)) < symprec))
exit
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,
'.'
384 do iop = 1, this%nops_nonsymmorphic
385 do isite = 1, n_sites
388 rsite = latt%fold_into_cell(rsite)
391 do isite_symm = 1, n_sites
392 if (all(abs(rsite - site_pos(:, isite_symm)) < symprec))
exit
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,
'.'
412 safe_allocate(this%ops(1))
415 this%nops_nonsymmorphic = 0
434 safe_allocate(lhs%ops(1:ubound(rhs%ops, dim=1)))
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))
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
465 safe_deallocate_a(this%ops)
466 safe_deallocate_a(this%non_symmorphic_ops)
483 integer,
intent(in) :: iop
484 real(real64),
intent(in) :: aa(1:3)
485 real(real64),
intent(out) :: bb(1:3)
489 assert(iop /= 0 .and. abs(iop) <= this%nops)
493 bb(1:3) = symm_op_apply_transpose_red(this%ops(abs(iop)), -aa(1:3))
495 bb(1:3) = symm_op_apply_transpose_red(this%ops(abs(iop)), aa(1:3))
503 integer pure function symmetries_space_group_number(this) result(number)
506 number = this%space_group
511 logical pure function symmetries_have_break_dir(this) result(have)
514 have = any(abs(this%breakdir(1:3)) > m_epsilon)
519 integer pure function symmetries_identity_index(this) result(index)
525 do iop = 1, this%nops
526 if (symm_op_is_identity(this%ops(iop)))
then
537 class(space_t),
intent(in) :: space
538 integer,
optional,
intent(in) :: iunit
539 type(namespace_t),
optional,
intent(in) :: namespace
545 call messages_print_with_emphasis(msg=
'Symmetries', iunit=iunit, namespace=namespace)
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)
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)
563 if (space%periodic_dim == 0)
then
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)
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)
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)
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
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))
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))
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))
598 call messages_info(1, iunit=iunit, namespace=namespace)
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
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))
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))
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))
619 call messages_info(1, iunit=iunit, namespace=namespace)
622 call messages_print_with_emphasis(iunit=iunit, namespace=namespace)
631 type(lattice_vectors_t),
intent(in) :: latt
632 integer,
intent(in) :: dim
638 do iop = 1, this%nops
639 call symm_op_build_cartesian(this%ops(iop), latt, dim)
641 do iop = 1, this%nops_nonsymmorphic
642 call symm_op_build_cartesian(this%non_symmorphic_ops(iop), latt, dim)
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)
657 integer :: isite, idir, dim4syms
658 real(real64),
allocatable :: position(:, :)
659 real(real64) :: lattice(1:3, 1:3)
663 assert(space%is_periodic())
665 dim4syms = min(3, space%dim)
667 safe_allocate(position(1:3, 1:n_sites))
669 do isite = 1, n_sites
670 position(1:3,isite) = m_zero
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))
678 position(1:dim4syms, isite) = position(1:dim4syms,isite) + m_half
684 lattice(1:space%periodic_dim, 1:space%periodic_dim) = latt%rlattice(1:space%periodic_dim, 1:space%periodic_dim)
686 lattice(:,:) = transpose(lattice(:,:))
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
693 lattice(idir, idir) = m_one
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)
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)
708 safe_deallocate_a(position)
717 integer,
intent(in) :: ll(3)
718 type(namespace_t),
intent(in) :: namespace
720 integer :: iop, iop2, ncompatible, nops_nonsymmorphic
721 real(real64) :: trans(3)
727 nops_nonsymmorphic = this%nops_nonsymmorphic
729 do while (iop <= nops_nonsymmorphic)
730 trans = symm_op_translation_vector_red(this%non_symmorphic_ops(iop))
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))
738 nops_nonsymmorphic = nops_nonsymmorphic - 1
744 if (ncompatible == 0)
then
749 call messages_print_with_emphasis(msg=
'Symmetries refinement', namespace=namespace)
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)
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)
763 this%nops_nonsymmorphic = nops_nonsymmorphic
764 this%nops = this%nops + ncompatible
766 call messages_print_with_emphasis(namespace=namespace)
NOTE: unfortunately, these routines use global variables shared among them.
real(real64), parameter, public m_zero
real(real64), parameter, public m_epsilon
real(real64), parameter, public m_half
This module is intended to contain "only mathematical" functions and procedures.
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
subroutine, public messages_experimental(name, namespace)
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
type(mpi_grp_t), public mpi_world
integer function, public parse_block(namespace, name, blk, check_varinfo_)
subroutine, public string_c_to_f(c_string, f_string)
convert a C string to a Fortran string
subroutine, public symm_op_copy(inp, outp)
logical pure function, public symm_op_has_translation(this, prec)
real(real64), parameter, public tol_translation
subroutine, public symm_op_init(this, rot, latt, dim, trans)
subroutine, public symmetries_update_lattice_vectors(this, latt, dim)
Updates the symmetry operations when lattice vectors are updated.
subroutine, public symmetries_apply_kpoint_red(this, iop, aa, bb)
integer pure function, public symmetries_space_group_number(this)
integer pure function, public symmetries_identity_index(this)
type(symmetries_t) function symmetries_constructor(namespace, space, latt, n_sites, site_pos, site_type, spherical_site)
subroutine, public symmetries_write_info(this, space, iunit, namespace)
real(real64), public symprec
integer pure function, public symmetries_number(this)
subroutine symmetries_copy(lhs, rhs)
logical pure function, public symmetries_have_break_dir(this)
type(spglibdataset) function, public symmetries_get_spg_dataset(namespace, space, latt, n_sites, site_pos, site_type)
Returns the SpglibDataset for the system.
subroutine symmetries_finalizer(this)
subroutine, public symmetries_use_compatible_nonsymmorphic(this, ll, namespace)
Detect if some non-symmorphic operations are compatible with the real-space grid If yes,...
subroutine init_identity()