27#include "./base/base_uses.f90"
32 LOGICAL,
PRIVATE,
PARAMETER :: debug_this_module = .true.
33 CHARACTER(len=*),
PARAMETER,
PRIVATE :: moduleN =
'molsym'
35 INTEGER,
PARAMETER :: maxcn = 20, &
37 maxses = 2*maxcn + 1, &
95 CHARACTER(LEN=4) :: point_group_symbol =
""
96 INTEGER :: point_group_order = -1
97 INTEGER :: ncn = -1, ndod = -1, ngroup = -1, nico = -1, &
98 nlin = -1, nsig = -1, nsn = -1, ntet = -1
99 LOGICAL :: cubic = .false., dgroup = .false., igroup = .false., &
100 invers = .false., linear = .false., maxis = .false., &
101 ogroup = .false., sgroup = .false., sigmad = .false., &
102 sigmah = .false., sigmav = .false., tgroup = .false.
103 REAL(kind=
dp) :: eps_geo = 0.0_dp
104 REAL(kind=
dp),
DIMENSION(3) :: center_of_mass = 0.0_dp, tenval = 0.0_dp
105 REAL(kind=
dp),
DIMENSION(3) :: x_axis = 0.0_dp, y_axis = 0.0_dp, z_axis = 0.0_dp
106 REAL(kind=
dp),
DIMENSION(:),
POINTER :: ain => null(), aw => null()
107 REAL(kind=
dp),
DIMENSION(3, 3) :: inptostd = 0.0_dp, rotmat = 0.0_dp, tenmat = 0.0_dp, tenvec = 0.0_dp
108 REAL(kind=
dp),
DIMENSION(3, maxsig) :: sig = 0.0_dp
109 REAL(kind=
dp),
DIMENSION(3, maxsec, 2:maxcn) :: sec = 0.0_dp
110 REAL(kind=
dp),
DIMENSION(3, maxses, 2:maxsn) :: ses = 0.0_dp
111 INTEGER,
DIMENSION(maxcn) :: nsec = -1
112 INTEGER,
DIMENSION(maxsn) :: nses = -1
113 INTEGER,
DIMENSION(:),
POINTER :: group_of => null(), llequatom => null(), nequatom => null(), &
114 symequ_list => null(), ulequatom => null()
129 INTEGER,
INTENT(IN) :: natoms
135 ALLOCATE (sym%ain(natoms), sym%aw(natoms), sym%group_of(natoms), sym%llequatom(natoms), &
136 sym%nequatom(natoms), sym%symequ_list(natoms), sym%ulequatom(natoms))
148 cpassert(
ASSOCIATED(sym))
150 IF (
ASSOCIATED(sym%aw))
THEN
153 IF (
ASSOCIATED(sym%ain))
THEN
156 IF (
ASSOCIATED(sym%group_of))
THEN
157 DEALLOCATE (sym%group_of)
159 IF (
ASSOCIATED(sym%llequatom))
THEN
160 DEALLOCATE (sym%llequatom)
162 IF (
ASSOCIATED(sym%nequatom))
THEN
163 DEALLOCATE (sym%nequatom)
165 IF (
ASSOCIATED(sym%symequ_list))
THEN
166 DEALLOCATE (sym%symequ_list)
168 IF (
ASSOCIATED(sym%ulequatom))
THEN
169 DEALLOCATE (sym%ulequatom)
188 SUBROUTINE addsec(n, a, sym)
189 INTEGER,
INTENT(IN) :: n
190 REAL(
dp),
DIMENSION(3),
INTENT(IN) :: a
194 REAL(
dp) :: length_of_a, scapro
195 REAL(
dp),
DIMENSION(3) :: d
197 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
198 d(:) = a(:)/length_of_a
201 DO isec = 1, sym%nsec(n)
202 scapro = sym%sec(1, isec, n)*d(1) + sym%sec(2, isec, n)*d(2) + sym%sec(3, isec, n)*d(3)
203 IF (abs(abs(scapro) - 1.0_dp) < sym%eps_geo)
RETURN
205 sym%ncn = max(sym%ncn, n)
208 cpassert(sym%nsec(n) < maxsec)
209 sym%nsec(1) = sym%nsec(1) + 1
210 sym%nsec(n) = sym%nsec(n) + 1
211 sym%sec(:, sym%nsec(n), n) = d(:)
213 END SUBROUTINE addsec
225 SUBROUTINE addses(n, a, sym)
226 INTEGER,
INTENT(IN) :: n
227 REAL(
dp),
DIMENSION(3),
INTENT(IN) :: a
231 REAL(
dp) :: length_of_a, scapro
232 REAL(
dp),
DIMENSION(3) :: d
234 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
235 d(:) = a(:)/length_of_a
238 DO ises = 1, sym%nses(n)
239 scapro = sym%ses(1, ises, n)*d(1) + sym%ses(2, ises, n)*d(2) + sym%ses(3, ises, n)*d(3)
240 IF (abs(abs(scapro) - 1.0_dp) < sym%eps_geo)
RETURN
242 sym%nsn = max(sym%nsn, n)
245 cpassert(sym%nses(n) < maxses)
246 sym%nses(1) = sym%nses(1) + 1
247 sym%nses(n) = sym%nses(n) + 1
248 sym%ses(:, sym%nses(n), n) = d(:)
250 END SUBROUTINE addses
261 SUBROUTINE addsig(a, sym)
262 REAL(
dp),
DIMENSION(3),
INTENT(IN) :: a
266 REAL(
dp) :: length_of_a, scapro
267 REAL(
dp),
DIMENSION(3) :: d
269 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
270 d(:) = a(:)/length_of_a
273 DO isig = 1, sym%nsig
274 scapro = sym%sig(1, isig)*d(1) + sym%sig(2, isig)*d(2) + sym%sig(3, isig)*d(3)
275 IF (abs(abs(scapro) - 1.0_dp) < sym%eps_geo)
RETURN
279 cpassert(sym%nsig < maxsig)
280 sym%nsig = sym%nsig + 1
281 sym%sig(:, sym%nsig) = d(:)
283 END SUBROUTINE addsig
292 SUBROUTINE atomsym(sym)
297 sym%point_group_symbol =
"R(3)"
305 END SUBROUTINE atomsym
315 SUBROUTINE axsym(coord, sym)
316 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
319 INTEGER :: iatom, isig, jatom, m, n, natoms, nx, &
322 REAL(
dp),
DIMENSION(3) :: a, b, d
326 IF ((sym%ncn == 2) .AND. (sym%nses(2) == 3))
THEN
327 IF (sym%nsn == 4)
THEN
329 phi =
angle(sym%ses(:, 1, sym%nsn), sym%z_axis(:))
331 CALL rotate_molecule(phi, d(:), sym, coord)
335 nx = naxis(sym%x_axis(:), coord, sym)
336 ny = naxis(sym%y_axis(:), coord, sym)
337 nz = naxis(sym%z_axis(:), coord, sym)
338 IF ((nx > ny) .AND. (nx > nz))
THEN
339 CALL rotate_molecule(-phi, sym%y_axis(:), sym, coord)
340 ELSE IF ((ny > nz) .AND. (ny > nx))
THEN
341 CALL rotate_molecule(phi, sym%x_axis(:), sym, coord)
345 phi =
angle(sym%sec(:, 1, sym%ncn), sym%z_axis(:))
347 CALL rotate_molecule(phi, d(:), sym, coord)
352 natoms =
SIZE(coord, 2)
354 a(:) = coord(:, iatom)
355 IF ((abs(a(1)) > sym%eps_geo) .OR. (abs(a(2)) > sym%eps_geo))
THEN
357 IF (caxis(2, a(:), sym, coord))
CALL addsec(2, a(:), sym)
359 IF (sigma(d(:), sym, coord))
CALL addsig(d(:), sym)
360 DO jatom = iatom + 1, natoms
361 b(:) = coord(:, jatom)
362 IF ((abs(b(1)) > sym%eps_geo) .OR. (abs(b(2)) > sym%eps_geo))
THEN
364 phi = 0.5_dp*
angle(a(:), b(:))
367 IF (caxis(2, b(:), sym, coord))
CALL addsec(2, b(:), sym)
369 IF (sigma(d(:), sym, coord))
CALL addsig(d(:), sym)
376 IF (sigma(sym%z_axis(:), sym, coord))
THEN
377 CALL addsig(sym%z_axis(:), sym)
382 IF (sym%nsec(2) > 1)
THEN
384 IF ((.NOT. sym%sigmah) .AND. (sym%nsig > 0)) sym%sigmad = .true.
386 IF ((.NOT. sym%sigmah) .AND. (sym%nsig > 0)) sym%sigmav = .true.
389 IF ((.NOT. sym%sigmah) .AND. (sym%nsn > 3)) sym%sgroup = .true.
395 IF ((sym%dgroup .AND. sym%sigmah) .OR. (sym%dgroup .AND. sym%sigmad) .OR. sym%sigmav)
THEN
397 DO isig = 1, sym%nsig
398 IF (abs(sym%sig(3, isig)) < sym%eps_geo)
THEN
399 n = nsigma(sym%sig(:, isig), sym, coord)
402 a(:) = sym%sig(:, isig)
408 IF (sym%sigmav .AND. (m == natoms))
THEN
409 phi =
angle(a(:), sym%x_axis(:))
412 phi =
angle(a(:), sym%y_axis(:))
415 CALL rotate_molecule(phi, d(:), sym, coord)
417 ELSE IF (sym%sigmah)
THEN
420 IF (abs(coord(3, iatom)) < sym%eps_geo)
THEN
421 n = naxis(coord(:, iatom), coord, sym)
424 a(:) = coord(:, iatom)
429 phi =
angle(a(:), sym%x_axis(:))
431 CALL rotate_molecule(phi, d(:), sym, coord)
446 SUBROUTINE build_symequ_list(sym, coord)
448 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
450 INTEGER :: i, iatom, icn, iequatom, incr, isec, &
451 ises, isig, isn, jatom, jcn, jsn, &
454 REAL(kind=
dp),
DIMENSION(3) :: a
456 natoms =
SIZE(coord, 2)
471 DO isig = 1, sym%nsig
473 iequatom = equatom(iatom, a(:), sym, coord)
474 IF ((iequatom > 0) .AND. (.NOT. in_symequ_list(iequatom, sym)))
THEN
475 sym%ulequatom(sym%ngroup) = sym%ulequatom(sym%ngroup) + 1
476 sym%symequ_list(sym%ulequatom(sym%ngroup)) = iequatom
477 sym%nequatom(sym%ngroup) = sym%nequatom(sym%ngroup) + 1
483 DO isec = 1, sym%nsec(icn)
485 IF (newse(jcn, icn))
THEN
486 phi = 2.0_dp*
pi*real(jcn, kind=
dp)/real(icn, kind=
dp)
487 a(:) =
rotate_vector(coord(:, iatom), phi, sym%sec(:, isec, icn))
488 iequatom = equatom(iatom, a(:), sym, coord)
489 IF ((iequatom > 0) .AND. (.NOT. in_symequ_list(iequatom, sym)))
THEN
490 sym%ulequatom(sym%ngroup) = sym%ulequatom(sym%ngroup) + 1
491 sym%symequ_list(sym%ulequatom(sym%ngroup)) = iequatom
492 sym%nequatom(sym%ngroup) = sym%nequatom(sym%ngroup) + 1
501 DO ises = 1, sym%nses(isn)
502 DO jsn = 1, isn - 1, incr
503 IF (newse(jsn, isn))
THEN
504 phi = 2.0_dp*
pi*real(jsn, kind=
dp)/real(isn, kind=
dp)
505 a(:) =
rotate_vector(coord(:, iatom), phi, sym%ses(:, ises, isn))
507 iequatom = equatom(iatom, a(:), sym, coord)
508 IF ((iequatom > 0) .AND. (.NOT. in_symequ_list(iequatom, sym)))
THEN
509 sym%ulequatom(sym%ngroup) = sym%ulequatom(sym%ngroup) + 1
510 sym%symequ_list(sym%ulequatom(sym%ngroup)) = iequatom
511 sym%nequatom(sym%ngroup) = sym%nequatom(sym%ngroup) + 1
519 IF (sym%ulequatom(sym%ngroup) == natoms)
EXIT
523 IF (.NOT. in_symequ_list(jatom, sym))
THEN
525 sym%ngroup = sym%ngroup + 1
526 sym%llequatom(sym%ngroup) = sym%ulequatom(sym%ngroup - 1) + 1
527 sym%ulequatom(sym%ngroup) = sym%llequatom(sym%ngroup)
528 sym%symequ_list(sym%llequatom(sym%ngroup)) = iatom
537 DO iequatom = sym%llequatom(i), sym%ulequatom(i)
538 sym%group_of(sym%symequ_list(iequatom)) = i
542 END SUBROUTINE build_symequ_list
556 FUNCTION caxis(n, a, sym, coord)
557 INTEGER,
INTENT(IN) :: n
558 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
560 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
563 INTEGER :: iatom, natoms
564 REAL(kind=
dp) :: length_of_a, phi
565 REAL(kind=
dp),
DIMENSION(3) :: b
568 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
571 natoms =
SIZE(coord, 2)
572 IF (length_of_a > sym%eps_geo)
THEN
574 phi = 2.0_dp*
pi/real(n,
dp)
579 b(:) = matmul(sym%rotmat(:, :), coord(:, iatom))
582 IF (equatom(iatom, b(:), sym, coord) == 0)
RETURN
599 SUBROUTINE cubsym(sym, coord, failed)
601 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
602 LOGICAL,
INTENT(INOUT) :: failed
604 INTEGER :: i, iatom, iax, ic3, isec, jatom, jc3, &
605 jsec, katom, natoms, nc3
606 REAL(kind=
dp) :: phi1, phi2, phidd, phidi, phiii
607 REAL(kind=
dp),
DIMENSION(3) :: a, b, c, d
610 phidd = atan(0.4_dp*sqrt(5.0_dp))
612 phidi = atan(3.0_dp - sqrt(5.0_dp))
617 natoms =
SIZE(coord, 2)
620 IF (caxis(3, coord(:, iatom), sym, coord))
THEN
621 CALL addsec(3, coord(:, iatom), sym)
622 IF (sym%nsec(3) > 1)
EXIT
628 IF (sym%nsec(3) < 2)
THEN
630 loop:
DO iatom = 1, natoms
631 a(:) = coord(:, iatom)
632 DO jatom = iatom + 1, natoms
633 DO katom = jatom + 1, natoms
634 IF ((abs(dist(coord(:, iatom), coord(:, jatom)) &
635 - dist(coord(:, iatom), coord(:, katom))) < sym%eps_geo) .AND. &
636 (abs(dist(coord(:, iatom), coord(:, jatom)) &
637 - dist(coord(:, jatom), coord(:, katom))) < sym%eps_geo))
THEN
638 b(:) = a(:) + coord(:, jatom) + coord(:, katom)
639 IF (caxis(3, b(:), sym, coord))
THEN
640 CALL addsec(3, b(:), sym)
641 IF (sym%nsec(3) > 1)
EXIT loop
651 IF (sym%nsec(3) < 2)
THEN
661 a(:) = sym%sec(:, ic3, 3)
664 d(:) = sym%sec(:, jc3, 3)
666 phi1 = 2.0_dp*real(i, kind=
dp)*
pi/3.0_dp
668 CALL addsec(3, b(:), sym)
674 IF (sym%nsec(3) > nc3)
THEN
679 IF (sym%nsec(3) == 4)
THEN
680 a(:) = sym%sec(:, 1, 3)
681 b(:) = sym%sec(:, 2, 3)
682 phi1 = 0.5_dp*
angle(a(:), b(:))
683 IF (phi1 < 0.5_dp*
pi) phi1 = phi1 - 0.5_dp*
pi
686 c(:) = sym%sec(:, 3, 3)
687 phi1 = 0.5_dp*
angle(a(:), c(:))
688 IF (phi1 < 0.5_dp*
pi) phi1 = phi1 - 0.5_dp*
pi
694 IF (caxis(3, a(:), sym, coord))
THEN
695 CALL addsec(3, a(:), sym)
697 phi2 = 0.5_dp*
pi - phi1
699 IF (caxis(3, a(:), sym, coord))
CALL addsec(3, a(:), sym)
702 IF (sym%nsec(3) > 4) cycle
704 ELSE IF (sym%nsec(3) /= 10)
THEN
720 DO isec = 1, sym%nsec(3)
722 a(:) = sym%sec(:, isec, 3)
723 DO jsec = isec + 1, sym%nsec(3)
724 phi1 = 0.5_dp*
angle(a(:), sym%sec(:, jsec, 3))
728 IF (sym%nsec(3) == 10)
THEN
730 IF (caxis(5, b(:), sym, coord))
THEN
731 CALL addsec(5, b(:), sym)
734 IF (caxis(5, b(:), sym, coord))
CALL addsec(5, b(:), sym)
740 phi2 = phi1 - 0.5_dp*real(i, kind=
dp)*
pi
742 IF (caxis(2, b(:), sym, coord))
THEN
743 CALL addsec(2, b(:), sym)
744 IF (sym%nsec(3) == 4)
THEN
745 IF (caxis(4, b(:), sym, coord))
CALL addsec(4, b(:), sym)
747 IF (saxis(2, b(:), sym, coord))
THEN
748 CALL addses(2, b(:), sym)
752 IF (sigma(b(:), sym, coord))
CALL addsig(b(:), sym)
756 IF (sigma(d(:), sym, coord))
CALL addsig(d(:), sym)
765 IF ((sym%ncn == 5) .AND. (sym%nsec(5) > 1))
THEN
767 ELSE IF ((sym%ncn == 4) .AND. (sym%nsec(4) > 1))
THEN
769 ELSE IF ((sym%ncn == 3) .AND. (sym%nsec(3) > 1))
THEN
771 IF ((.NOT. sym%invers) .AND. (sym%nsig > 0)) sym%sigmad = .true.
780 phi1 =
angle(sym%sec(:, 1, iax), sym%z_axis(:))
782 CALL rotate_molecule(phi1, d(:), sym, coord)
784 a(:) = sym%sec(:, 2, iax)
786 phi2 =
angle(a(:), sym%x_axis(:))
788 CALL rotate_molecule(phi2, d(:), sym, coord)
790 END SUBROUTINE cubsym
805 FUNCTION equatom(atoma, a, sym, coord)
806 INTEGER,
INTENT(IN) :: atoma
807 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
809 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
812 INTEGER :: iatom, natoms
815 natoms =
SIZE(coord, 2)
817 IF ((abs(sym%ain(iatom) - sym%ain(atoma)) < tiny(0.0_dp)) .AND. &
818 (abs(a(1) - coord(1, iatom)) < sym%eps_geo) .AND. &
819 (abs(a(2) - coord(2, iatom)) < sym%eps_geo) .AND. &
820 (abs(a(3) - coord(3, iatom)) < sym%eps_geo))
THEN
835 SUBROUTINE get_point_group_order(sym)
838 INTEGER :: icn, incr, isec, ises, isn, jcn, jsn
842 sym%point_group_order = 1 + sym%nsig
846 DO isec = 1, sym%nsec(icn)
848 IF (newse(jcn, icn)) sym%point_group_order = sym%point_group_order + 1
861 DO ises = 1, sym%nses(isn)
862 DO jsn = 1, isn - 1, incr
863 IF (newse(jsn, isn)) sym%point_group_order = sym%point_group_order + 1
868 END SUBROUTINE get_point_group_order
877 SUBROUTINE get_point_group_symbol(sym)
880 CHARACTER(LEN=4) ::
degree
884 sym%point_group_symbol =
"D*h"
886 sym%point_group_symbol =
"C*v"
888 ELSE IF (sym%cubic)
THEN
890 sym%point_group_symbol =
"I"
891 ELSE IF (sym%ogroup)
THEN
892 sym%point_group_symbol =
"O"
893 ELSE IF (sym%tgroup)
THEN
894 sym%point_group_symbol =
"T"
897 sym%point_group_symbol = trim(sym%point_group_symbol)//
"h"
898 ELSE IF (sym%sigmad)
THEN
899 sym%point_group_symbol = trim(sym%point_group_symbol)//
"d"
901 ELSE IF (sym%maxis)
THEN
903 WRITE (
degree,
"(I3)") sym%ncn
904 sym%point_group_symbol =
"D"//trim(adjustl(
degree))
906 sym%point_group_symbol = trim(sym%point_group_symbol)//
"h"
907 ELSE IF (sym%sigmad)
THEN
908 sym%point_group_symbol = trim(sym%point_group_symbol)//
"d"
910 ELSE IF (sym%sgroup)
THEN
911 WRITE (
degree,
"(I3)") sym%nsn
912 sym%point_group_symbol =
"S"//trim(adjustl(
degree))
914 WRITE (
degree,
"(I3)") sym%ncn
915 sym%point_group_symbol =
"C"//trim(adjustl(
degree))
917 sym%point_group_symbol = trim(sym%point_group_symbol)//
"h"
918 ELSE IF (sym%sigmav)
THEN
919 sym%point_group_symbol = trim(sym%point_group_symbol)//
"v"
924 sym%point_group_symbol =
"Ci"
925 ELSE IF (sym%sigmah)
THEN
926 sym%point_group_symbol =
"Cs"
928 sym%point_group_symbol =
"C1"
932 END SUBROUTINE get_point_group_symbol
943 SUBROUTINE init_symmetry(sym, atype, weight)
945 INTEGER,
DIMENSION(:),
INTENT(IN) :: atype
946 REAL(kind=
dp),
DIMENSION(:),
INTENT(IN) :: weight
948 INTEGER :: i, iatom, natoms
951 sym%x_axis = (/1.0_dp, 0.0_dp, 0.0_dp/)
952 sym%y_axis = (/0.0_dp, 1.0_dp, 0.0_dp/)
953 sym%z_axis = (/0.0_dp, 0.0_dp, 1.0_dp/)
956 sym%sec(:, :, :) = 0.0_dp
957 sym%ses(:, :, :) = 0.0_dp
958 sym%sig(:, :) = 0.0_dp
960 sym%center_of_mass(:) = 0.0_dp
984 natoms =
SIZE(sym%nequatom)
990 sym%symequ_list(i) = i
994 sym%point_group_order = -1
997 sym%aw(iatom) = weight(iatom)
1001 sym%ain(:) = real(atype(:), kind=
dp) + sym%aw(:)
1006 END SUBROUTINE init_symmetry
1017 FUNCTION in_symequ_list(atoma, sym)
1018 INTEGER,
INTENT(IN) :: atoma
1020 LOGICAL :: in_symequ_list
1024 in_symequ_list = .false.
1026 DO iatom = 1, sym%ulequatom(sym%ngroup)
1027 IF (sym%symequ_list(iatom) == atoma)
THEN
1028 in_symequ_list = .true.
1033 END FUNCTION in_symequ_list
1043 SUBROUTINE lowsym(sym, coord)
1045 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1047 REAL(kind=
dp) :: phi
1048 REAL(kind=
dp),
DIMENSION(3) :: d
1050 IF (sym%nsn == 2)
THEN
1053 phi =
angle(sym%ses(:, 1, 2), sym%z_axis(:))
1055 CALL rotate_molecule(phi, d(:), sym, coord)
1056 ELSE IF (sym%nsig == 1)
THEN
1059 phi =
angle(sym%sig(:, 1), sym%z_axis(:))
1061 CALL rotate_molecule(phi, d(:), sym, coord)
1064 END SUBROUTINE lowsym
1079 REAL(kind=
dp),
INTENT(IN) :: eps_geo
1080 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1081 INTEGER,
DIMENSION(:),
INTENT(IN) :: atype
1082 REAL(kind=
dp),
DIMENSION(:),
INTENT(IN) :: weight
1084 CHARACTER(LEN=*),
PARAMETER :: routinen =
'molecular_symmetry'
1086 INTEGER :: handle, natoms
1088 CALL timeset(routinen, handle)
1091 natoms =
SIZE(coord, 2)
1093 sym%eps_geo = eps_geo
1096 CALL init_symmetry(sym, atype, weight)
1098 IF (natoms == 1)
THEN
1103 CALL moleculesym(sym, coord)
1105 CALL get_point_group_symbol(sym)
1109 IF (.NOT. sym%linear)
CALL get_point_group_order(sym)
1112 CALL build_symequ_list(sym, coord)
1114 CALL timestop(handle)
1126 SUBROUTINE moleculesym(sym, coord)
1128 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1130 INTEGER :: icn, isec, isn
1132 REAL(kind=
dp) :: eps_tenval
1136 eps_tenval = 0.01_dp*sym%eps_geo
1141 IF ((sym%tenval(3) - sym%tenval(1)) < eps_tenval)
THEN
1144 ELSE IF ((sym%tenval(3) - sym%tenval(2)) < eps_tenval)
THEN
1147 IF (sym%tenval(1) < eps_tenval) sym%linear = .true.
1148 CALL tracar(sym, coord, (/3, 1, 2/))
1151 CALL tracar(sym, coord, (/1, 2, 3/))
1158 CALL cubsym(sym, coord, failed)
1164 ELSE IF (sym%linear)
THEN
1167 IF (saxis(2, sym%z_axis(:), sym, coord))
THEN
1177 IF (caxis(icn, sym%z_axis(:), sym, coord))
CALL addsec(icn, sym%z_axis(:), sym)
1178 IF (caxis(icn, sym%x_axis(:), sym, coord))
CALL addsec(icn, sym%x_axis(:), sym)
1179 IF (caxis(icn, sym%y_axis(:), sym, coord))
CALL addsec(icn, sym%y_axis(:), sym)
1184 IF (saxis(isn, sym%z_axis(:), sym, coord))
CALL addses(isn, sym%z_axis(:), sym)
1185 IF (saxis(isn, sym%x_axis(:), sym, coord))
CALL addses(isn, sym%x_axis(:), sym)
1186 IF (saxis(isn, sym%y_axis(:), sym, coord))
CALL addses(isn, sym%y_axis(:), sym)
1190 IF (sigma(sym%z_axis(:), sym, coord))
CALL addsig(sym%z_axis(:), sym)
1191 IF (sigma(sym%x_axis(:), sym, coord))
CALL addsig(sym%x_axis(:), sym)
1192 IF (sigma(sym%y_axis(:), sym, coord))
CALL addsig(sym%y_axis(:), sym)
1195 IF ((sym%ncn > 1) .OR. (sym%nsn > 3))
THEN
1197 CALL axsym(coord, sym)
1200 CALL lowsym(sym, coord)
1205 IF (.NOT. failed)
EXIT
1210 IF (.NOT. sym%linear)
THEN
1212 DO isec = 1, sym%nsec(icn)
1213 IF (saxis(icn, sym%sec(:, isec, icn), sym, coord)) &
1214 CALL addses(icn, sym%sec(:, isec, icn), sym)
1215 IF (saxis(2*icn, sym%sec(:, isec, icn), sym, coord)) &
1216 CALL addses(2*icn, sym%sec(:, isec, icn), sym)
1222 IF (sym%nses(2) > 0)
THEN
1223 sym%nses(1) = sym%nses(1) - sym%nses(2)
1225 CALL addses(2, sym%z_axis(:), sym)
1228 END SUBROUTINE moleculesym
1240 FUNCTION naxis(a, coord, sym)
1241 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1242 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1246 INTEGER :: iatom, natoms
1247 REAL(kind=
dp) :: length_of_a, length_of_b, scapro
1248 REAL(kind=
dp),
DIMENSION(3) :: a_norm, b, b_norm
1251 natoms =
SIZE(coord, 2)
1253 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1256 IF (length_of_a > sym%eps_geo)
THEN
1258 DO iatom = 1, natoms
1259 b(:) = coord(:, iatom)
1260 length_of_b = sqrt(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
1262 IF (length_of_b < sym%eps_geo)
THEN
1265 a_norm = a(:)/length_of_a
1266 b_norm = b(:)/length_of_b
1267 scapro = a_norm(1)*b_norm(1) + a_norm(2)*b_norm(2) + a_norm(3)*b_norm(3)
1268 IF (abs(abs(scapro) - 1.0_dp) < sym%eps_geo) naxis = naxis + 1
1286 FUNCTION newse(se1, se2)
1287 INTEGER,
INTENT(IN) :: se1, se2
1294 DO ise = 2, min(se1, se2)
1295 IF ((
modulo(se1, ise) == 0) .AND. (
modulo(se2, ise) == 0))
THEN
1314 FUNCTION nsigma(a, sym, coord)
1315 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1317 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1320 INTEGER :: iatom, natoms
1321 REAL(kind=
dp) :: length_of_a, length_of_b, scapro
1322 REAL(kind=
dp),
DIMENSION(3) :: a_norm, b, b_norm
1326 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1329 IF (length_of_a > sym%eps_geo)
THEN
1330 natoms =
SIZE(coord, 2)
1331 DO iatom = 1, natoms
1332 b(:) = coord(:, iatom)
1333 length_of_b = sqrt(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
1335 IF (length_of_b < sym%eps_geo)
THEN
1338 a_norm = a(:)/length_of_a
1339 b_norm = b(:)/length_of_b
1340 scapro = a_norm(1)*b_norm(1) + a_norm(2)*b_norm(2) + a_norm(3)*b_norm(3)
1341 IF (abs(scapro) < sym%eps_geo) nsigma = nsigma + 1
1356 SUBROUTINE outse(se, eps)
1357 REAL(kind=
dp),
DIMENSION(3),
INTENT(INOUT) :: se
1358 REAL(kind=
dp),
INTENT(IN) :: eps
1362 IF (abs(se(3)) < eps)
THEN
1363 IF (abs(se(1)) < eps)
THEN
1366 IF (se(1) < 0.0_dp) se(1:2) = -se(1:2)
1369 IF (se(3) < 0.0_dp) se(:) = -se(:)
1372 END SUBROUTINE outse
1390 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(in) :: coord
1391 INTEGER,
DIMENSION(:),
INTENT(in) :: atype
1392 CHARACTER(LEN=*),
DIMENSION(:),
INTENT(in) :: element
1393 INTEGER,
DIMENSION(:),
INTENT(in) :: z
1394 REAL(kind=
dp),
DIMENSION(:),
INTENT(in) :: weight
1395 INTEGER,
INTENT(IN) :: iw, plevel
1397 REAL(kind=
dp),
PARAMETER :: formatmaxval = 1.0e5_dp
1399 CHARACTER(LEN=3) :: string
1400 INTEGER :: i, icn, iequatom, isec, ises, isig, isn, &
1401 j, nequatom, secount
1402 INTEGER,
ALLOCATABLE,
DIMENSION(:) :: equatomlist, idxlist
1405 WRITE (iw,
"(/,(T2,A))") &
1406 "MOLSYM| Molecular symmetry information", &
1408 WRITE (iw,
"(T2,A,T77,A4)") &
1409 "MOLSYM| Point group", adjustr(sym%point_group_symbol)
1410 IF (sym%point_group_order > -1)
THEN
1411 WRITE (iw,
"(T2,A,T77,I4)") &
1412 "MOLSYM| Group order ", sym%point_group_order
1415 IF (mod(plevel, 10) == 1)
THEN
1416 WRITE (iw,
"(T2,A)") &
1418 "MOLSYM| Groups of atoms equivalent by symmetry"
1419 WRITE (iw,
"(T2,A,T31,A)") &
1420 "MOLSYM| Group number",
"Group members (atomic indices)"
1421 DO i = 1, sym%ngroup
1422 nequatom = sym%ulequatom(i) - sym%llequatom(i) + 1
1423 cpassert(nequatom > 0)
1424 ALLOCATE (equatomlist(nequatom), idxlist(nequatom))
1425 equatomlist(1:nequatom) = sym%symequ_list(sym%llequatom(i):sym%ulequatom(i))
1427 CALL sort(equatomlist, nequatom, idxlist)
1428 WRITE (iw,
"(T2,A,T10,I5,T16,I6,T31,10(1X,I4),/,(T2,'MOLSYM|',T31,10(1X,I4)))") &
1429 "MOLSYM|", i, nequatom, (equatomlist(iequatom), iequatom=1, nequatom)
1430 DEALLOCATE (equatomlist, idxlist)
1434 IF (mod(plevel/100, 10) == 1)
THEN
1436 WRITE (iw,
"(T2,A,/,T2,A,/,T2,A,T31,A,T45,A,T60,A,T75,A)") &
1438 "MOLSYM| Symmetry elements", &
1439 "MOLSYM| Element number",
"Type",
"X",
"Y",
"Z"
1441 WRITE (iw,
"(T2,A,T12,I5,T19,I5,T32,A1,T36,3(1X,F14.8))") &
1442 "MOLSYM|", secount, secount,
"E", 0.0_dp, 0.0_dp, 0.0_dp
1445 DO isig = 1, sym%nsig
1446 secount = secount + 1
1447 CALL outse(sym%sig(:, isig), sym%eps_geo)
1448 WRITE (iw,
"(T2,A,T12,I5,T19,I5,T32,A3,T36,3(1X,F14.8))") &
1449 "MOLSYM|", secount, isig, string, (sym%sig(i, isig), i=1, 3)
1454 WRITE (string,
"(A1,I1)")
"C", icn
1456 WRITE (string,
"(A1,I2)")
"C", icn
1458 DO isec = 1, sym%nsec(icn)
1459 secount = secount + 1
1460 CALL outse(sym%sec(:, isec, icn), sym%eps_geo)
1461 WRITE (iw,
"(T2,A,T12,I5,T19,I5,T32,A3,T36,3(1X,F14.8))") &
1462 "MOLSYM|", secount, isec, string, (sym%sec(i, isec, icn), i=1, 3)
1468 WRITE (string,
"(A3)")
"i "
1469 ELSE IF (isn < 10)
THEN
1470 WRITE (string,
"(A1,I1)")
"S", isn
1472 WRITE (string,
"(A1,I2)")
"S", isn
1474 DO ises = 1, sym%nses(isn)
1475 secount = secount + 1
1476 CALL outse(sym%ses(:, ises, isn), sym%eps_geo)
1477 WRITE (iw,
"(T2,A,T12,I5,T19,I5,T32,A3,T36,3(1X,F14.8))") &
1478 "MOLSYM|", secount, ises, string, (sym%ses(i, ises, icn), i=1, 3)
1483 IF (mod(plevel/10, 10) == 1 .AND.
SIZE(coord, 2) > 1)
THEN
1485 WRITE (iw,
"(T2,A,/,T2,A,/,T2,A,T43,A,T58,A,T73,A)") &
1487 "MOLSYM| Moments of the molecular inertia tensor [a.u.]", &
1488 "MOLSYM|",
"I(x)",
"I(y)",
"I(z)"
1490 IF (maxval(sym%tenmat) >= formatmaxval)
THEN
1492 WRITE (iw,
"(T2,A,T32,A,T36,3(1X,ES14.4))") &
1493 "MOLSYM|",
"I("//string(i:i)//
")", (sym%tenmat(i, j), j=1, 3)
1497 WRITE (iw,
"(T2,A,T32,A,T36,3(1X,F14.8))") &
1498 "MOLSYM|",
"I("//string(i:i)//
")", (sym%tenmat(i, j), j=1, 3)
1501 WRITE (iw,
"(T2,A)") &
1503 "MOLSYM| Principal moments and axes of inertia [a.u.]"
1504 IF (maxval(sym%tenmat) >= formatmaxval)
THEN
1505 WRITE (iw,
"(T2,A,T36,3(1X,ES14.4))") &
1506 "MOLSYM|", (sym%tenval(i), i=1, 3)
1508 WRITE (iw,
"(T2,A,T36,3(1X,F14.8))") &
1509 "MOLSYM|", (sym%tenval(i), i=1, 3)
1512 WRITE (iw,
"(T2,A,T32,A,T36,3(1X,F14.8))") &
1513 "MOLSYM|", string(i:i), (sym%tenvec(i, j), j=1, 3)
1517 IF (mod(plevel, 10) == 1)
THEN
1519 CALL write_coordinates(coord, atype, element, z, weight, iw)
1535 SUBROUTINE rotate_molecule(phi, a, sym, coord)
1536 REAL(kind=
dp),
INTENT(IN) :: phi
1537 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1539 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1542 REAL(kind=
dp) :: length_of_a
1545 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1546 IF (length_of_a > sym%eps_geo)
THEN
1552 coord(:, :) = matmul(sym%rotmat(:, :), coord(:, :))
1555 sym%sig(:, 1:sym%nsig) = matmul(sym%rotmat(:, :), sym%sig(:, 1:sym%nsig))
1559 sym%sec(:, 1:sym%nsec(i), i) = matmul(sym%rotmat(:, :), sym%sec(:, 1:sym%nsec(i), i))
1564 sym%ses(:, 1:sym%nses(i), i) = matmul(sym%rotmat(:, :), sym%ses(:, 1:sym%nses(i), i))
1568 sym%inptostd(:, :) = matmul(sym%rotmat(:, :), sym%inptostd(:, :))
1572 END SUBROUTINE rotate_molecule
1586 FUNCTION saxis(n, a, sym, coord)
1587 INTEGER,
INTENT(IN) :: n
1588 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1590 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1593 INTEGER :: iatom, natoms
1594 REAL(kind=
dp) :: length_of_a, phi
1595 REAL(kind=
dp),
DIMENSION(3) :: b
1599 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1601 natoms =
SIZE(coord, 2)
1604 IF (length_of_a > sym%eps_geo)
THEN
1606 phi = 2.0_dp*
pi/real(n, kind=
dp)
1609 DO iatom = 1, natoms
1611 b(:) = matmul(sym%rotmat(:, :), coord(:, iatom))
1615 IF (equatom(iatom, b(:), sym, coord) == 0)
RETURN
1634 FUNCTION sigma(a, sym, coord)
1635 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1637 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1640 INTEGER :: iatom, natoms
1641 REAL(kind=
dp) :: length_of_a
1642 REAL(kind=
dp),
DIMENSION(3) :: b
1646 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1649 IF (length_of_a > sym%eps_geo)
THEN
1650 natoms =
SIZE(coord, 2)
1651 DO iatom = 1, natoms
1655 IF (equatom(iatom, b(:), sym, coord) == 0)
RETURN
1672 SUBROUTINE tensor(sym, coord)
1674 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1680 natoms =
SIZE(coord, 2)
1681 sym%center_of_mass(:) = matmul(coord(:, 1:natoms), sym%aw(1:natoms))/sum(sym%aw(1:natoms))
1684 coord(:, 1:natoms) = coord(:, 1:natoms) - spread(sym%center_of_mass(:), 2, natoms)
1688 sym%tenmat(1, 1) = dot_product(sym%aw(1:natoms), (coord(2, 1:natoms)**2 + coord(3, 1:natoms)**2))
1689 sym%tenmat(2, 2) = dot_product(sym%aw(1:natoms), (coord(3, 1:natoms)**2 + coord(1, 1:natoms)**2))
1690 sym%tenmat(3, 3) = dot_product(sym%aw(1:natoms), (coord(1, 1:natoms)**2 + coord(2, 1:natoms)**2))
1692 sym%tenmat(1, 2) = -dot_product(sym%aw(1:natoms), (coord(1, 1:natoms)*coord(2, 1:natoms)))
1693 sym%tenmat(1, 3) = -dot_product(sym%aw(1:natoms), (coord(1, 1:natoms)*coord(3, 1:natoms)))
1694 sym%tenmat(2, 3) = -dot_product(sym%aw(1:natoms), (coord(2, 1:natoms)*coord(3, 1:natoms)))
1697 sym%tenmat(2, 1) = sym%tenmat(1, 2)
1698 sym%tenmat(3, 1) = sym%tenmat(1, 3)
1699 sym%tenmat(3, 2) = sym%tenmat(2, 3)
1702 CALL jacobi(sym%tenmat(:, :), sym%tenval(:), sym%tenvec(:, :))
1705 sym%tenvec(:, 3) =
vector_product(sym%tenvec(:, 1), sym%tenvec(:, 2))
1707 tt = sqrt(sym%tenval(1)**2 + sym%tenval(2)**2 + sym%tenval(3)**2)
1727 SUBROUTINE tracar(sym, coord, idx)
1729 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1730 INTEGER,
DIMENSION(3),
INTENT(IN) ::
idx
1732 INTEGER :: iatom, natom
1733 REAL(kind=
dp),
DIMENSION(3, 3) :: tenvec
1736 natom =
SIZE(coord, 2)
1737 tenvec = transpose(sym%tenvec)
1739 coord(
idx, iatom) = matmul(tenvec, coord(:, iatom))
1743 sym%inptostd(
idx, :) = tenvec
1745 END SUBROUTINE tracar
1754 FUNCTION dist(a, b)
RESULT(d)
1755 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a, b
1758 d = sqrt(sum((a - b)**2))
1772 SUBROUTINE write_coordinates(coord, atype, element, z, weight, iw)
1773 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(in) :: coord
1774 INTEGER,
DIMENSION(:),
INTENT(in) :: atype
1775 CHARACTER(LEN=*),
DIMENSION(:),
INTENT(in) :: element
1776 INTEGER,
DIMENSION(:),
INTENT(in) :: z
1777 REAL(kind=
dp),
DIMENSION(:),
INTENT(in) :: weight
1778 INTEGER,
INTENT(IN) :: iw
1780 INTEGER :: iatom, natom
1783 natom =
SIZE(coord, 2)
1784 WRITE (unit=iw, fmt=
"(T2,A)") &
1786 "MOLSYM| Atomic coordinates of the standard orientation in BOHR"
1787 WRITE (unit=iw, fmt=
"(T2,A,T37,A,T51,A,T65,A,T77,A)") &
1788 "MOLSYM| Atom Kind Element",
"X",
"Y",
"Z",
"Mass"
1790 WRITE (unit=iw, fmt=
"(T2,A,I7,1X,I4,1X,A2,1X,I4,3(1X,F13.8),1X,F9.4)") &
1791 "MOLSYM|", iatom, atype(iatom), adjustl(element(iatom)), z(iatom), &
1792 coord(1:3, iatom), weight(iatom)
1794 WRITE (unit=iw, fmt=
"(T2,A)") &
1796 "MOLSYM| Atomic coordinates of the standard orientation in ANGSTROM"
1797 WRITE (unit=iw, fmt=
"(T2,A,T37,A,T51,A,T65,A,T77,A)") &
1798 "MOLSYM| Atom Kind Element",
"X",
"Y",
"Z",
"Mass"
1800 WRITE (unit=iw, fmt=
"(T2,A,I7,1X,I4,1X,A2,1X,I4,3(1X,F13.8),1X,F9.4)") &
1801 "MOLSYM|", iatom, atype(iatom), adjustl(element(iatom)), z(iatom), &
1802 coord(1:3, iatom)*
angstrom, weight(iatom)
1806 END SUBROUTINE write_coordinates
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
static GRID_HOST_DEVICE int idx(const orbital a)
Return coset index of given orbital angular momentum.
Defines the basic variable types.
integer, parameter, public dp
Definition of mathematical constants and functions.
real(kind=dp), parameter, public pi
real(kind=dp), parameter, public degree
Collection of simple mathematical functions and subroutines.
subroutine, public jacobi(a, d, v)
Jacobi matrix diagonalization. The eigenvalues are returned in vector d and the eigenvectors are retu...
pure real(kind=dp) function, dimension(3), public reflect_vector(a, b)
Reflection of the vector a through a mirror plane defined by the normal vector b. The reflected vecto...
pure real(kind=dp) function, public angle(a, b)
Calculation of the angle between the vectors a and b. The angle is returned in radians.
pure subroutine, public build_rotmat(phi, a, rotmat)
The rotation matrix rotmat which rotates a vector about a rotation axis defined by the vector a is bu...
pure real(kind=dp) function, dimension(3), public rotate_vector(a, phi, b)
Rotation of the vector a about an rotation axis defined by the vector b. The rotation angle is phi (r...
pure real(kind=dp) function, dimension(3), public vector_product(a, b)
Calculation of the vector product c = a x b.
Molecular symmetry routines.
subroutine, public molecular_symmetry(sym, eps_geo, coord, atype, weight)
Main program for symmetry analysis.
subroutine, public release_molsym(sym)
release an object of molsym type
subroutine create_molsym(sym, natoms)
Create an object of molsym type.
subroutine, public print_symmetry(sym, coord, atype, element, z, weight, iw, plevel)
Print the output of the symmetry analysis.
Definition of physical constants:
real(kind=dp), parameter, public angstrom
All kind of helpful little routines.
Container for information about molecular symmetry.