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
97 INTEGER :: ncn, ndod, ngroup, nico, nlin, nsig, nsn, ntet
98 LOGICAL :: cubic, dgroup, igroup, invers, linear, maxis, &
99 ogroup, sgroup, sigmad, sigmah, sigmav, tgroup
100 REAL(KIND=
dp) :: eps_geo
101 REAL(KIND=
dp),
DIMENSION(3) :: center_of_mass, tenval
102 REAL(KIND=
dp),
DIMENSION(3) :: x_axis, y_axis, z_axis
103 REAL(KIND=
dp),
DIMENSION(:),
POINTER :: ain, aw
104 REAL(KIND=
dp),
DIMENSION(3, 3) :: inptostd, rotmat, tenmat, tenvec
105 REAL(KIND=
dp),
DIMENSION(3, maxsig) :: sig
106 REAL(KIND=
dp),
DIMENSION(3, maxsec, 2:maxcn) :: sec
107 REAL(KIND=
dp),
DIMENSION(3, maxses, 2:maxsn) :: ses
108 INTEGER,
DIMENSION(maxcn) :: nsec
109 INTEGER,
DIMENSION(maxsn) :: nses
110 INTEGER,
DIMENSION(:),
POINTER :: group_of, llequatom, nequatom, &
111 symequ_list, ulequatom
124 SUBROUTINE create_molsym(sym, natoms)
125 TYPE(molsym_type),
POINTER :: sym
126 INTEGER,
INTENT(IN) :: natoms
132 ALLOCATE (sym%ain(natoms), sym%aw(natoms), sym%group_of(natoms), sym%llequatom(natoms), &
133 sym%nequatom(natoms), sym%symequ_list(natoms), sym%ulequatom(natoms))
135 END SUBROUTINE create_molsym
143 TYPE(molsym_type),
POINTER :: sym
145 cpassert(
ASSOCIATED(sym))
147 IF (
ASSOCIATED(sym%aw))
THEN
150 IF (
ASSOCIATED(sym%ain))
THEN
153 IF (
ASSOCIATED(sym%group_of))
THEN
154 DEALLOCATE (sym%group_of)
156 IF (
ASSOCIATED(sym%llequatom))
THEN
157 DEALLOCATE (sym%llequatom)
159 IF (
ASSOCIATED(sym%nequatom))
THEN
160 DEALLOCATE (sym%nequatom)
162 IF (
ASSOCIATED(sym%symequ_list))
THEN
163 DEALLOCATE (sym%symequ_list)
165 IF (
ASSOCIATED(sym%ulequatom))
THEN
166 DEALLOCATE (sym%ulequatom)
185 SUBROUTINE addsec(n, a, sym)
186 INTEGER,
INTENT(IN) :: n
187 REAL(
dp),
DIMENSION(3),
INTENT(IN) :: a
188 TYPE(molsym_type),
INTENT(inout) :: sym
191 REAL(
dp) :: length_of_a, scapro
192 REAL(
dp),
DIMENSION(3) :: d
194 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
195 d(:) = a(:)/length_of_a
198 DO isec = 1, sym%nsec(n)
199 scapro = sym%sec(1, isec, n)*d(1) + sym%sec(2, isec, n)*d(2) + sym%sec(3, isec, n)*d(3)
200 IF (abs(abs(scapro) - 1.0_dp) < sym%eps_geo)
RETURN
202 sym%ncn = max(sym%ncn, n)
205 cpassert(sym%nsec(n) < maxsec)
206 sym%nsec(1) = sym%nsec(1) + 1
207 sym%nsec(n) = sym%nsec(n) + 1
208 sym%sec(:, sym%nsec(n), n) = d(:)
210 END SUBROUTINE addsec
222 SUBROUTINE addses(n, a, sym)
223 INTEGER,
INTENT(IN) :: n
224 REAL(
dp),
DIMENSION(3),
INTENT(IN) :: a
225 TYPE(molsym_type),
INTENT(inout) :: sym
228 REAL(
dp) :: length_of_a, scapro
229 REAL(
dp),
DIMENSION(3) :: d
231 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
232 d(:) = a(:)/length_of_a
235 DO ises = 1, sym%nses(n)
236 scapro = sym%ses(1, ises, n)*d(1) + sym%ses(2, ises, n)*d(2) + sym%ses(3, ises, n)*d(3)
237 IF (abs(abs(scapro) - 1.0_dp) < sym%eps_geo)
RETURN
239 sym%nsn = max(sym%nsn, n)
242 cpassert(sym%nses(n) < maxses)
243 sym%nses(1) = sym%nses(1) + 1
244 sym%nses(n) = sym%nses(n) + 1
245 sym%ses(:, sym%nses(n), n) = d(:)
247 END SUBROUTINE addses
258 SUBROUTINE addsig(a, sym)
259 REAL(
dp),
DIMENSION(3),
INTENT(IN) :: a
260 TYPE(molsym_type),
INTENT(inout) :: sym
263 REAL(
dp) :: length_of_a, scapro
264 REAL(
dp),
DIMENSION(3) :: d
266 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
267 d(:) = a(:)/length_of_a
270 DO isig = 1, sym%nsig
271 scapro = sym%sig(1, isig)*d(1) + sym%sig(2, isig)*d(2) + sym%sig(3, isig)*d(3)
272 IF (abs(abs(scapro) - 1.0_dp) < sym%eps_geo)
RETURN
276 cpassert(sym%nsig < maxsig)
277 sym%nsig = sym%nsig + 1
278 sym%sig(:, sym%nsig) = d(:)
280 END SUBROUTINE addsig
289 SUBROUTINE atomsym(sym)
290 TYPE(molsym_type),
INTENT(inout) :: sym
294 sym%point_group_symbol =
"R(3)"
302 END SUBROUTINE atomsym
312 SUBROUTINE axsym(coord, sym)
313 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
314 TYPE(molsym_type),
INTENT(inout) :: sym
316 INTEGER :: iatom, isig, jatom, m, n, natoms, nx, &
319 REAL(
dp),
DIMENSION(3) :: a, b, d
323 IF ((sym%ncn == 2) .AND. (sym%nses(2) == 3))
THEN
324 IF (sym%nsn == 4)
THEN
326 phi =
angle(sym%ses(:, 1, sym%nsn), sym%z_axis(:))
328 CALL rotate_molecule(phi, d(:), sym, coord)
332 nx = naxis(sym%x_axis(:), coord, sym)
333 ny = naxis(sym%y_axis(:), coord, sym)
334 nz = naxis(sym%z_axis(:), coord, sym)
335 IF ((nx > ny) .AND. (nx > nz))
THEN
336 CALL rotate_molecule(-phi, sym%y_axis(:), sym, coord)
337 ELSE IF ((ny > nz) .AND. (ny > nx))
THEN
338 CALL rotate_molecule(phi, sym%x_axis(:), sym, coord)
342 phi =
angle(sym%sec(:, 1, sym%ncn), sym%z_axis(:))
344 CALL rotate_molecule(phi, d(:), sym, coord)
349 natoms =
SIZE(coord, 2)
351 a(:) = coord(:, iatom)
352 IF ((abs(a(1)) > sym%eps_geo) .OR. (abs(a(2)) > sym%eps_geo))
THEN
354 IF (caxis(2, a(:), sym, coord))
CALL addsec(2, a(:), sym)
356 IF (sigma(d(:), sym, coord))
CALL addsig(d(:), sym)
357 DO jatom = iatom + 1, natoms
358 b(:) = coord(:, jatom)
359 IF ((abs(b(1)) > sym%eps_geo) .OR. (abs(b(2)) > sym%eps_geo))
THEN
361 phi = 0.5_dp*
angle(a(:), b(:))
364 IF (caxis(2, b(:), sym, coord))
CALL addsec(2, b(:), sym)
366 IF (sigma(d(:), sym, coord))
CALL addsig(d(:), sym)
373 IF (sigma(sym%z_axis(:), sym, coord))
THEN
374 CALL addsig(sym%z_axis(:), sym)
379 IF (sym%nsec(2) > 1)
THEN
381 IF ((.NOT. sym%sigmah) .AND. (sym%nsig > 0)) sym%sigmad = .true.
383 IF ((.NOT. sym%sigmah) .AND. (sym%nsig > 0)) sym%sigmav = .true.
386 IF ((.NOT. sym%sigmah) .AND. (sym%nsn > 3)) sym%sgroup = .true.
392 IF ((sym%dgroup .AND. sym%sigmah) .OR. (sym%dgroup .AND. sym%sigmad) .OR. sym%sigmav)
THEN
394 DO isig = 1, sym%nsig
395 IF (abs(sym%sig(3, isig)) < sym%eps_geo)
THEN
396 n = nsigma(sym%sig(:, isig), sym, coord)
399 a(:) = sym%sig(:, isig)
405 IF (sym%sigmav .AND. (m == natoms))
THEN
406 phi =
angle(a(:), sym%x_axis(:))
409 phi =
angle(a(:), sym%y_axis(:))
412 CALL rotate_molecule(phi, d(:), sym, coord)
414 ELSE IF (sym%sigmah)
THEN
417 IF (abs(coord(3, iatom)) < sym%eps_geo)
THEN
418 n = naxis(coord(:, iatom), coord, sym)
421 a(:) = coord(:, iatom)
426 phi =
angle(a(:), sym%x_axis(:))
428 CALL rotate_molecule(phi, d(:), sym, coord)
443 SUBROUTINE build_symequ_list(sym, coord)
444 TYPE(molsym_type),
INTENT(inout) :: sym
445 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
447 INTEGER :: i, iatom, icn, iequatom, incr, isec, &
448 ises, isig, isn, jatom, jcn, jsn, &
451 REAL(kind=
dp),
DIMENSION(3) :: a
453 natoms =
SIZE(coord, 2)
468 DO isig = 1, sym%nsig
470 iequatom = equatom(iatom, a(:), sym, coord)
471 IF ((iequatom > 0) .AND. (.NOT. in_symequ_list(iequatom, sym)))
THEN
472 sym%ulequatom(sym%ngroup) = sym%ulequatom(sym%ngroup) + 1
473 sym%symequ_list(sym%ulequatom(sym%ngroup)) = iequatom
474 sym%nequatom(sym%ngroup) = sym%nequatom(sym%ngroup) + 1
480 DO isec = 1, sym%nsec(icn)
482 IF (newse(jcn, icn))
THEN
483 phi = 2.0_dp*
pi*real(jcn, kind=
dp)/real(icn, kind=
dp)
484 a(:) =
rotate_vector(coord(:, iatom), phi, sym%sec(:, isec, icn))
485 iequatom = equatom(iatom, a(:), sym, coord)
486 IF ((iequatom > 0) .AND. (.NOT. in_symequ_list(iequatom, sym)))
THEN
487 sym%ulequatom(sym%ngroup) = sym%ulequatom(sym%ngroup) + 1
488 sym%symequ_list(sym%ulequatom(sym%ngroup)) = iequatom
489 sym%nequatom(sym%ngroup) = sym%nequatom(sym%ngroup) + 1
498 DO ises = 1, sym%nses(isn)
499 DO jsn = 1, isn - 1, incr
500 IF (newse(jsn, isn))
THEN
501 phi = 2.0_dp*
pi*real(jsn, kind=
dp)/real(isn, kind=
dp)
502 a(:) =
rotate_vector(coord(:, iatom), phi, sym%ses(:, ises, isn))
504 iequatom = equatom(iatom, a(:), sym, coord)
505 IF ((iequatom > 0) .AND. (.NOT. in_symequ_list(iequatom, sym)))
THEN
506 sym%ulequatom(sym%ngroup) = sym%ulequatom(sym%ngroup) + 1
507 sym%symequ_list(sym%ulequatom(sym%ngroup)) = iequatom
508 sym%nequatom(sym%ngroup) = sym%nequatom(sym%ngroup) + 1
516 IF (sym%ulequatom(sym%ngroup) == natoms)
EXIT
520 IF (.NOT. in_symequ_list(jatom, sym))
THEN
522 sym%ngroup = sym%ngroup + 1
523 sym%llequatom(sym%ngroup) = sym%ulequatom(sym%ngroup - 1) + 1
524 sym%ulequatom(sym%ngroup) = sym%llequatom(sym%ngroup)
525 sym%symequ_list(sym%llequatom(sym%ngroup)) = iatom
534 DO iequatom = sym%llequatom(i), sym%ulequatom(i)
535 sym%group_of(sym%symequ_list(iequatom)) = i
539 END SUBROUTINE build_symequ_list
553 FUNCTION caxis(n, a, sym, coord)
554 INTEGER,
INTENT(IN) :: n
555 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
556 TYPE(molsym_type),
INTENT(inout) :: sym
557 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
560 INTEGER :: iatom, natoms
561 REAL(kind=
dp) :: length_of_a, phi
562 REAL(kind=
dp),
DIMENSION(3) :: b
565 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
568 natoms =
SIZE(coord, 2)
569 IF (length_of_a > sym%eps_geo)
THEN
571 phi = 2.0_dp*
pi/real(n,
dp)
576 b(:) = matmul(sym%rotmat(:, :), coord(:, iatom))
579 IF (equatom(iatom, b(:), sym, coord) == 0)
RETURN
596 SUBROUTINE cubsym(sym, coord, failed)
597 TYPE(molsym_type),
INTENT(inout) :: sym
598 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
599 LOGICAL,
INTENT(INOUT) :: failed
601 INTEGER :: i, iatom, iax, ic3, isec, jatom, jc3, &
602 jsec, katom, natoms, nc3
603 REAL(kind=
dp) :: phi1, phi2, phidd, phidi, phiii
604 REAL(kind=
dp),
DIMENSION(3) :: a, b, c, d
607 phidd = atan(0.4_dp*sqrt(5.0_dp))
609 phidi = atan(3.0_dp - sqrt(5.0_dp))
614 natoms =
SIZE(coord, 2)
617 IF (caxis(3, coord(:, iatom), sym, coord))
THEN
618 CALL addsec(3, coord(:, iatom), sym)
619 IF (sym%nsec(3) > 1)
EXIT
625 IF (sym%nsec(3) < 2)
THEN
627 loop:
DO iatom = 1, natoms
628 a(:) = coord(:, iatom)
629 DO jatom = iatom + 1, natoms
630 DO katom = jatom + 1, natoms
631 IF ((abs(dist(coord(:, iatom), coord(:, jatom)) &
632 - dist(coord(:, iatom), coord(:, katom))) < sym%eps_geo) .AND. &
633 (abs(dist(coord(:, iatom), coord(:, jatom)) &
634 - dist(coord(:, jatom), coord(:, katom))) < sym%eps_geo))
THEN
635 b(:) = a(:) + coord(:, jatom) + coord(:, katom)
636 IF (caxis(3, b(:), sym, coord))
THEN
637 CALL addsec(3, b(:), sym)
638 IF (sym%nsec(3) > 1)
EXIT loop
648 IF (sym%nsec(3) < 2)
THEN
658 a(:) = sym%sec(:, ic3, 3)
661 d(:) = sym%sec(:, jc3, 3)
663 phi1 = 2.0_dp*real(i, kind=
dp)*
pi/3.0_dp
665 CALL addsec(3, b(:), sym)
671 IF (sym%nsec(3) > nc3)
THEN
676 IF (sym%nsec(3) == 4)
THEN
677 a(:) = sym%sec(:, 1, 3)
678 b(:) = sym%sec(:, 2, 3)
679 phi1 = 0.5_dp*
angle(a(:), b(:))
680 IF (phi1 < 0.5_dp*
pi) phi1 = phi1 - 0.5_dp*
pi
683 c(:) = sym%sec(:, 3, 3)
684 phi1 = 0.5_dp*
angle(a(:), c(:))
685 IF (phi1 < 0.5_dp*
pi) phi1 = phi1 - 0.5_dp*
pi
691 IF (caxis(3, a(:), sym, coord))
THEN
692 CALL addsec(3, a(:), sym)
694 phi2 = 0.5_dp*
pi - phi1
696 IF (caxis(3, a(:), sym, coord))
CALL addsec(3, a(:), sym)
699 IF (sym%nsec(3) > 4) cycle
701 ELSE IF (sym%nsec(3) /= 10)
THEN
717 DO isec = 1, sym%nsec(3)
719 a(:) = sym%sec(:, isec, 3)
720 DO jsec = isec + 1, sym%nsec(3)
721 phi1 = 0.5_dp*
angle(a(:), sym%sec(:, jsec, 3))
725 IF (sym%nsec(3) == 10)
THEN
727 IF (caxis(5, b(:), sym, coord))
THEN
728 CALL addsec(5, b(:), sym)
731 IF (caxis(5, b(:), sym, coord))
CALL addsec(5, b(:), sym)
737 phi2 = phi1 - 0.5_dp*real(i, kind=
dp)*
pi
739 IF (caxis(2, b(:), sym, coord))
THEN
740 CALL addsec(2, b(:), sym)
741 IF (sym%nsec(3) == 4)
THEN
742 IF (caxis(4, b(:), sym, coord))
CALL addsec(4, b(:), sym)
744 IF (saxis(2, b(:), sym, coord))
THEN
745 CALL addses(2, b(:), sym)
749 IF (sigma(b(:), sym, coord))
CALL addsig(b(:), sym)
753 IF (sigma(d(:), sym, coord))
CALL addsig(d(:), sym)
762 IF ((sym%ncn == 5) .AND. (sym%nsec(5) > 1))
THEN
764 ELSE IF ((sym%ncn == 4) .AND. (sym%nsec(4) > 1))
THEN
766 ELSE IF ((sym%ncn == 3) .AND. (sym%nsec(3) > 1))
THEN
768 IF ((.NOT. sym%invers) .AND. (sym%nsig > 0)) sym%sigmad = .true.
777 phi1 =
angle(sym%sec(:, 1, iax), sym%z_axis(:))
779 CALL rotate_molecule(phi1, d(:), sym, coord)
781 a(:) = sym%sec(:, 2, iax)
783 phi2 =
angle(a(:), sym%x_axis(:))
785 CALL rotate_molecule(phi2, d(:), sym, coord)
787 END SUBROUTINE cubsym
802 FUNCTION equatom(atoma, a, sym, coord)
803 INTEGER,
INTENT(IN) :: atoma
804 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
805 TYPE(molsym_type),
INTENT(inout) :: sym
806 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
809 INTEGER :: iatom, natoms
812 natoms =
SIZE(coord, 2)
814 IF ((abs(sym%ain(iatom) - sym%ain(atoma)) < tiny(0.0_dp)) .AND. &
815 (abs(a(1) - coord(1, iatom)) < sym%eps_geo) .AND. &
816 (abs(a(2) - coord(2, iatom)) < sym%eps_geo) .AND. &
817 (abs(a(3) - coord(3, iatom)) < sym%eps_geo))
THEN
832 SUBROUTINE get_point_group_order(sym)
833 TYPE(molsym_type),
INTENT(inout) :: sym
835 INTEGER :: icn, incr, isec, ises, isn, jcn, jsn
839 sym%point_group_order = 1 + sym%nsig
843 DO isec = 1, sym%nsec(icn)
845 IF (newse(jcn, icn)) sym%point_group_order = sym%point_group_order + 1
858 DO ises = 1, sym%nses(isn)
859 DO jsn = 1, isn - 1, incr
860 IF (newse(jsn, isn)) sym%point_group_order = sym%point_group_order + 1
865 END SUBROUTINE get_point_group_order
874 SUBROUTINE get_point_group_symbol(sym)
875 TYPE(molsym_type),
INTENT(inout) :: sym
877 CHARACTER(LEN=4) :: degree
881 sym%point_group_symbol =
"D*h"
883 sym%point_group_symbol =
"C*v"
885 ELSE IF (sym%cubic)
THEN
887 sym%point_group_symbol =
"I"
888 ELSE IF (sym%ogroup)
THEN
889 sym%point_group_symbol =
"O"
890 ELSE IF (sym%tgroup)
THEN
891 sym%point_group_symbol =
"T"
894 sym%point_group_symbol = trim(sym%point_group_symbol)//
"h"
895 ELSE IF (sym%sigmad)
THEN
896 sym%point_group_symbol = trim(sym%point_group_symbol)//
"d"
898 ELSE IF (sym%maxis)
THEN
900 WRITE (degree,
"(I3)") sym%ncn
901 sym%point_group_symbol =
"D"//trim(adjustl(degree))
903 sym%point_group_symbol = trim(sym%point_group_symbol)//
"h"
904 ELSE IF (sym%sigmad)
THEN
905 sym%point_group_symbol = trim(sym%point_group_symbol)//
"d"
907 ELSE IF (sym%sgroup)
THEN
908 WRITE (degree,
"(I3)") sym%nsn
909 sym%point_group_symbol =
"S"//trim(adjustl(degree))
911 WRITE (degree,
"(I3)") sym%ncn
912 sym%point_group_symbol =
"C"//trim(adjustl(degree))
914 sym%point_group_symbol = trim(sym%point_group_symbol)//
"h"
915 ELSE IF (sym%sigmav)
THEN
916 sym%point_group_symbol = trim(sym%point_group_symbol)//
"v"
921 sym%point_group_symbol =
"Ci"
922 ELSE IF (sym%sigmah)
THEN
923 sym%point_group_symbol =
"Cs"
925 sym%point_group_symbol =
"C1"
929 END SUBROUTINE get_point_group_symbol
940 SUBROUTINE init_symmetry(sym, atype, weight)
941 TYPE(molsym_type),
INTENT(inout) :: sym
942 INTEGER,
DIMENSION(:),
INTENT(IN) :: atype
943 REAL(kind=
dp),
DIMENSION(:),
INTENT(IN) :: weight
945 INTEGER :: i, iatom, natoms
948 sym%x_axis = (/1.0_dp, 0.0_dp, 0.0_dp/)
949 sym%y_axis = (/0.0_dp, 1.0_dp, 0.0_dp/)
950 sym%z_axis = (/0.0_dp, 0.0_dp, 1.0_dp/)
953 sym%sec(:, :, :) = 0.0_dp
954 sym%ses(:, :, :) = 0.0_dp
955 sym%sig(:, :) = 0.0_dp
957 sym%center_of_mass(:) = 0.0_dp
981 natoms =
SIZE(sym%nequatom)
987 sym%symequ_list(i) = i
991 sym%point_group_order = -1
994 sym%aw(iatom) = weight(iatom)
998 sym%ain(:) = real(atype(:), kind=
dp) + sym%aw(:)
1001 CALL unit_matrix(sym%inptostd(:, :))
1003 END SUBROUTINE init_symmetry
1014 FUNCTION in_symequ_list(atoma, sym)
1015 INTEGER,
INTENT(IN) :: atoma
1016 TYPE(molsym_type),
INTENT(inout) :: sym
1017 LOGICAL :: in_symequ_list
1021 in_symequ_list = .false.
1023 DO iatom = 1, sym%ulequatom(sym%ngroup)
1024 IF (sym%symequ_list(iatom) == atoma)
THEN
1025 in_symequ_list = .true.
1030 END FUNCTION in_symequ_list
1040 SUBROUTINE lowsym(sym, coord)
1041 TYPE(molsym_type),
INTENT(inout) :: sym
1042 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1044 REAL(kind=
dp) :: phi
1045 REAL(kind=
dp),
DIMENSION(3) :: d
1047 IF (sym%nsn == 2)
THEN
1050 phi =
angle(sym%ses(:, 1, 2), sym%z_axis(:))
1052 CALL rotate_molecule(phi, d(:), sym, coord)
1053 ELSE IF (sym%nsig == 1)
THEN
1056 phi =
angle(sym%sig(:, 1), sym%z_axis(:))
1058 CALL rotate_molecule(phi, d(:), sym, coord)
1061 END SUBROUTINE lowsym
1075 TYPE(molsym_type),
POINTER :: sym
1076 REAL(kind=
dp),
INTENT(IN) :: eps_geo
1077 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1078 INTEGER,
DIMENSION(:),
INTENT(IN) :: atype
1079 REAL(kind=
dp),
DIMENSION(:),
INTENT(IN) :: weight
1081 CHARACTER(LEN=*),
PARAMETER :: routinen =
'molecular_symmetry'
1083 INTEGER :: handle, natoms
1085 CALL timeset(routinen, handle)
1088 natoms =
SIZE(coord, 2)
1089 CALL create_molsym(sym, natoms)
1090 sym%eps_geo = eps_geo
1093 CALL init_symmetry(sym, atype, weight)
1095 IF (natoms == 1)
THEN
1100 CALL moleculesym(sym, coord)
1102 CALL get_point_group_symbol(sym)
1106 IF (.NOT. sym%linear)
CALL get_point_group_order(sym)
1109 CALL build_symequ_list(sym, coord)
1111 CALL timestop(handle)
1123 SUBROUTINE moleculesym(sym, coord)
1124 TYPE(molsym_type),
INTENT(inout) :: sym
1125 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1127 INTEGER :: icn, isec, isn
1129 REAL(kind=
dp) :: eps_tenval
1133 eps_tenval = 0.01_dp*sym%eps_geo
1138 IF ((sym%tenval(3) - sym%tenval(1)) < eps_tenval)
THEN
1141 ELSE IF ((sym%tenval(3) - sym%tenval(2)) < eps_tenval)
THEN
1144 IF (sym%tenval(1) < eps_tenval) sym%linear = .true.
1145 CALL tracar(sym, coord, (/3, 1, 2/))
1148 CALL tracar(sym, coord, (/1, 2, 3/))
1155 CALL cubsym(sym, coord, failed)
1161 ELSE IF (sym%linear)
THEN
1164 IF (saxis(2, sym%z_axis(:), sym, coord))
THEN
1174 IF (caxis(icn, sym%z_axis(:), sym, coord))
CALL addsec(icn, sym%z_axis(:), sym)
1175 IF (caxis(icn, sym%x_axis(:), sym, coord))
CALL addsec(icn, sym%x_axis(:), sym)
1176 IF (caxis(icn, sym%y_axis(:), sym, coord))
CALL addsec(icn, sym%y_axis(:), sym)
1181 IF (saxis(isn, sym%z_axis(:), sym, coord))
CALL addses(isn, sym%z_axis(:), sym)
1182 IF (saxis(isn, sym%x_axis(:), sym, coord))
CALL addses(isn, sym%x_axis(:), sym)
1183 IF (saxis(isn, sym%y_axis(:), sym, coord))
CALL addses(isn, sym%y_axis(:), sym)
1187 IF (sigma(sym%z_axis(:), sym, coord))
CALL addsig(sym%z_axis(:), sym)
1188 IF (sigma(sym%x_axis(:), sym, coord))
CALL addsig(sym%x_axis(:), sym)
1189 IF (sigma(sym%y_axis(:), sym, coord))
CALL addsig(sym%y_axis(:), sym)
1192 IF ((sym%ncn > 1) .OR. (sym%nsn > 3))
THEN
1194 CALL axsym(coord, sym)
1197 CALL lowsym(sym, coord)
1202 IF (.NOT. failed)
EXIT
1207 IF (.NOT. sym%linear)
THEN
1209 DO isec = 1, sym%nsec(icn)
1210 IF (saxis(icn, sym%sec(:, isec, icn), sym, coord)) &
1211 CALL addses(icn, sym%sec(:, isec, icn), sym)
1212 IF (saxis(2*icn, sym%sec(:, isec, icn), sym, coord)) &
1213 CALL addses(2*icn, sym%sec(:, isec, icn), sym)
1219 IF (sym%nses(2) > 0)
THEN
1220 sym%nses(1) = sym%nses(1) - sym%nses(2)
1222 CALL addses(2, sym%z_axis(:), sym)
1225 END SUBROUTINE moleculesym
1237 FUNCTION naxis(a, coord, sym)
1238 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1239 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1240 TYPE(molsym_type),
INTENT(inout) :: sym
1243 INTEGER :: iatom, natoms
1244 REAL(kind=
dp) :: length_of_a, length_of_b, scapro
1245 REAL(kind=
dp),
DIMENSION(3) :: a_norm, b, b_norm
1248 natoms =
SIZE(coord, 2)
1250 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1253 IF (length_of_a > sym%eps_geo)
THEN
1255 DO iatom = 1, natoms
1256 b(:) = coord(:, iatom)
1257 length_of_b = sqrt(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
1259 IF (length_of_b < sym%eps_geo)
THEN
1262 a_norm = a(:)/length_of_a
1263 b_norm = b(:)/length_of_b
1264 scapro = a_norm(1)*b_norm(1) + a_norm(2)*b_norm(2) + a_norm(3)*b_norm(3)
1265 IF (abs(abs(scapro) - 1.0_dp) < sym%eps_geo) naxis = naxis + 1
1283 FUNCTION newse(se1, se2)
1284 INTEGER,
INTENT(IN) :: se1, se2
1291 DO ise = 2, min(se1, se2)
1292 IF ((
modulo(se1, ise) == 0) .AND. (
modulo(se2, ise) == 0))
THEN
1311 FUNCTION nsigma(a, sym, coord)
1312 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1313 TYPE(molsym_type),
INTENT(inout) :: sym
1314 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1317 INTEGER :: iatom, natoms
1318 REAL(kind=
dp) :: length_of_a, length_of_b, scapro
1319 REAL(kind=
dp),
DIMENSION(3) :: a_norm, b, b_norm
1323 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1326 IF (length_of_a > sym%eps_geo)
THEN
1327 natoms =
SIZE(coord, 2)
1328 DO iatom = 1, natoms
1329 b(:) = coord(:, iatom)
1330 length_of_b = sqrt(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
1332 IF (length_of_b < sym%eps_geo)
THEN
1335 a_norm = a(:)/length_of_a
1336 b_norm = b(:)/length_of_b
1337 scapro = a_norm(1)*b_norm(1) + a_norm(2)*b_norm(2) + a_norm(3)*b_norm(3)
1338 IF (abs(scapro) < sym%eps_geo) nsigma = nsigma + 1
1353 SUBROUTINE outse(se, eps)
1354 REAL(kind=
dp),
DIMENSION(3),
INTENT(INOUT) :: se
1355 REAL(kind=
dp),
INTENT(IN) :: eps
1359 IF (abs(se(3)) < eps)
THEN
1360 IF (abs(se(1)) < eps)
THEN
1363 IF (se(1) < 0.0_dp) se(1:2) = -se(1:2)
1366 IF (se(3) < 0.0_dp) se(:) = -se(:)
1369 END SUBROUTINE outse
1386 TYPE(molsym_type),
INTENT(inout) :: sym
1387 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(in) :: coord
1388 INTEGER,
DIMENSION(:),
INTENT(in) :: atype
1389 CHARACTER(LEN=*),
DIMENSION(:),
INTENT(in) :: element
1390 INTEGER,
DIMENSION(:),
INTENT(in) :: z
1391 REAL(kind=
dp),
DIMENSION(:),
INTENT(in) :: weight
1392 INTEGER,
INTENT(IN) :: iw, plevel
1394 REAL(kind=
dp),
PARAMETER :: formatmaxval = 1.0e5_dp
1396 CHARACTER(LEN=3) :: string
1397 INTEGER :: i, icn, iequatom, isec, ises, isig, isn, &
1398 j, nequatom, secount
1399 INTEGER,
ALLOCATABLE,
DIMENSION(:) :: equatomlist, idxlist
1402 WRITE (iw,
"(/,(T2,A))") &
1403 "MOLSYM| Molecular symmetry information", &
1405 WRITE (iw,
"(T2,A,T77,A4)") &
1406 "MOLSYM| Point group", adjustr(sym%point_group_symbol)
1407 IF (sym%point_group_order > -1)
THEN
1408 WRITE (iw,
"(T2,A,T77,I4)") &
1409 "MOLSYM| Group order ", sym%point_group_order
1412 IF (mod(plevel, 10) == 1)
THEN
1413 WRITE (iw,
"(T2,A)") &
1415 "MOLSYM| Groups of atoms equivalent by symmetry"
1416 WRITE (iw,
"(T2,A,T31,A)") &
1417 "MOLSYM| Group number",
"Group members (atomic indices)"
1418 DO i = 1, sym%ngroup
1419 nequatom = sym%ulequatom(i) - sym%llequatom(i) + 1
1420 cpassert(nequatom > 0)
1421 ALLOCATE (equatomlist(nequatom), idxlist(nequatom))
1422 equatomlist(1:nequatom) = sym%symequ_list(sym%llequatom(i):sym%ulequatom(i))
1424 CALL sort(equatomlist, nequatom, idxlist)
1425 WRITE (iw,
"(T2,A,T10,I5,T16,I6,T31,10(1X,I4),/,(T2,'MOLSYM|',T31,10(1X,I4)))") &
1426 "MOLSYM|", i, nequatom, (equatomlist(iequatom), iequatom=1, nequatom)
1427 DEALLOCATE (equatomlist, idxlist)
1431 IF (mod(plevel/100, 10) == 1)
THEN
1433 WRITE (iw,
"(T2,A,/,T2,A,/,T2,A,T31,A,T45,A,T60,A,T75,A)") &
1435 "MOLSYM| Symmetry elements", &
1436 "MOLSYM| Element number",
"Type",
"X",
"Y",
"Z"
1438 WRITE (iw,
"(T2,A,T12,I5,T19,I5,T32,A1,T36,3(1X,F14.8))") &
1439 "MOLSYM|", secount, secount,
"E", 0.0_dp, 0.0_dp, 0.0_dp
1442 DO isig = 1, sym%nsig
1443 secount = secount + 1
1444 CALL outse(sym%sig(:, isig), sym%eps_geo)
1445 WRITE (iw,
"(T2,A,T12,I5,T19,I5,T32,A3,T36,3(1X,F14.8))") &
1446 "MOLSYM|", secount, isig, string, (sym%sig(i, isig), i=1, 3)
1451 WRITE (string,
"(A1,I1)")
"C", icn
1453 WRITE (string,
"(A1,I2)")
"C", icn
1455 DO isec = 1, sym%nsec(icn)
1456 secount = secount + 1
1457 CALL outse(sym%sec(:, isec, icn), sym%eps_geo)
1458 WRITE (iw,
"(T2,A,T12,I5,T19,I5,T32,A3,T36,3(1X,F14.8))") &
1459 "MOLSYM|", secount, isec, string, (sym%sec(i, isec, icn), i=1, 3)
1465 WRITE (string,
"(A3)")
"i "
1466 ELSE IF (isn < 10)
THEN
1467 WRITE (string,
"(A1,I1)")
"S", isn
1469 WRITE (string,
"(A1,I2)")
"S", isn
1471 DO ises = 1, sym%nses(isn)
1472 secount = secount + 1
1473 CALL outse(sym%ses(:, ises, isn), sym%eps_geo)
1474 WRITE (iw,
"(T2,A,T12,I5,T19,I5,T32,A3,T36,3(1X,F14.8))") &
1475 "MOLSYM|", secount, ises, string, (sym%ses(i, ises, icn), i=1, 3)
1480 IF (mod(plevel/10, 10) == 1 .AND.
SIZE(coord, 2) > 1)
THEN
1482 WRITE (iw,
"(T2,A,/,T2,A,/,T2,A,T43,A,T58,A,T73,A)") &
1484 "MOLSYM| Moments of the molecular inertia tensor [a.u.]", &
1485 "MOLSYM|",
"I(x)",
"I(y)",
"I(z)"
1487 IF (maxval(sym%tenmat) >= formatmaxval)
THEN
1489 WRITE (iw,
"(T2,A,T32,A,T36,3(1X,ES14.4))") &
1490 "MOLSYM|",
"I("//string(i:i)//
")", (sym%tenmat(i, j), j=1, 3)
1494 WRITE (iw,
"(T2,A,T32,A,T36,3(1X,F14.8))") &
1495 "MOLSYM|",
"I("//string(i:i)//
")", (sym%tenmat(i, j), j=1, 3)
1498 WRITE (iw,
"(T2,A)") &
1500 "MOLSYM| Principal moments and axes of inertia [a.u.]"
1501 IF (maxval(sym%tenmat) >= formatmaxval)
THEN
1502 WRITE (iw,
"(T2,A,T36,3(1X,ES14.4))") &
1503 "MOLSYM|", (sym%tenval(i), i=1, 3)
1505 WRITE (iw,
"(T2,A,T36,3(1X,F14.8))") &
1506 "MOLSYM|", (sym%tenval(i), i=1, 3)
1509 WRITE (iw,
"(T2,A,T32,A,T36,3(1X,F14.8))") &
1510 "MOLSYM|", string(i:i), (sym%tenvec(i, j), j=1, 3)
1514 IF (mod(plevel, 10) == 1)
THEN
1516 CALL write_coordinates(coord, atype, element, z, weight, iw)
1532 SUBROUTINE rotate_molecule(phi, a, sym, coord)
1533 REAL(kind=
dp),
INTENT(IN) :: phi
1534 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1535 TYPE(molsym_type),
INTENT(inout) :: sym
1536 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1539 REAL(kind=
dp) :: length_of_a
1542 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1543 IF (length_of_a > sym%eps_geo)
THEN
1549 coord(:, :) = matmul(sym%rotmat(:, :), coord(:, :))
1552 sym%sig(:, 1:sym%nsig) = matmul(sym%rotmat(:, :), sym%sig(:, 1:sym%nsig))
1556 sym%sec(:, 1:sym%nsec(i), i) = matmul(sym%rotmat(:, :), sym%sec(:, 1:sym%nsec(i), i))
1561 sym%ses(:, 1:sym%nses(i), i) = matmul(sym%rotmat(:, :), sym%ses(:, 1:sym%nses(i), i))
1565 sym%inptostd(:, :) = matmul(sym%rotmat(:, :), sym%inptostd(:, :))
1569 END SUBROUTINE rotate_molecule
1583 FUNCTION saxis(n, a, sym, coord)
1584 INTEGER,
INTENT(IN) :: n
1585 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1586 TYPE(molsym_type),
INTENT(inout) :: sym
1587 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1590 INTEGER :: iatom, natoms
1591 REAL(kind=
dp) :: length_of_a, phi
1592 REAL(kind=
dp),
DIMENSION(3) :: b
1596 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1598 natoms =
SIZE(coord, 2)
1601 IF (length_of_a > sym%eps_geo)
THEN
1603 phi = 2.0_dp*
pi/real(n, kind=
dp)
1606 DO iatom = 1, natoms
1608 b(:) = matmul(sym%rotmat(:, :), coord(:, iatom))
1612 IF (equatom(iatom, b(:), sym, coord) == 0)
RETURN
1631 FUNCTION sigma(a, sym, coord)
1632 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a
1633 TYPE(molsym_type),
INTENT(inout) :: sym
1634 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1637 INTEGER :: iatom, natoms
1638 REAL(kind=
dp) :: length_of_a
1639 REAL(kind=
dp),
DIMENSION(3) :: b
1643 length_of_a = sqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
1646 IF (length_of_a > sym%eps_geo)
THEN
1647 natoms =
SIZE(coord, 2)
1648 DO iatom = 1, natoms
1652 IF (equatom(iatom, b(:), sym, coord) == 0)
RETURN
1669 SUBROUTINE tensor(sym, coord)
1670 TYPE(molsym_type),
INTENT(inout) :: sym
1671 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1677 natoms =
SIZE(coord, 2)
1678 sym%center_of_mass(:) = matmul(coord(:, 1:natoms), sym%aw(1:natoms))/sum(sym%aw(1:natoms))
1681 coord(:, 1:natoms) = coord(:, 1:natoms) - spread(sym%center_of_mass(:), 2, natoms)
1685 sym%tenmat(1, 1) = dot_product(sym%aw(1:natoms), (coord(2, 1:natoms)**2 + coord(3, 1:natoms)**2))
1686 sym%tenmat(2, 2) = dot_product(sym%aw(1:natoms), (coord(3, 1:natoms)**2 + coord(1, 1:natoms)**2))
1687 sym%tenmat(3, 3) = dot_product(sym%aw(1:natoms), (coord(1, 1:natoms)**2 + coord(2, 1:natoms)**2))
1689 sym%tenmat(1, 2) = -dot_product(sym%aw(1:natoms), (coord(1, 1:natoms)*coord(2, 1:natoms)))
1690 sym%tenmat(1, 3) = -dot_product(sym%aw(1:natoms), (coord(1, 1:natoms)*coord(3, 1:natoms)))
1691 sym%tenmat(2, 3) = -dot_product(sym%aw(1:natoms), (coord(2, 1:natoms)*coord(3, 1:natoms)))
1694 sym%tenmat(2, 1) = sym%tenmat(1, 2)
1695 sym%tenmat(3, 1) = sym%tenmat(1, 3)
1696 sym%tenmat(3, 2) = sym%tenmat(2, 3)
1699 CALL jacobi(sym%tenmat(:, :), sym%tenval(:), sym%tenvec(:, :))
1702 sym%tenvec(:, 3) =
vector_product(sym%tenvec(:, 1), sym%tenvec(:, 2))
1704 tt = sqrt(sym%tenval(1)**2 + sym%tenval(2)**2 + sym%tenval(3)**2)
1724 SUBROUTINE tracar(sym, coord, idx)
1725 TYPE(molsym_type),
INTENT(inout) :: sym
1726 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(inout) :: coord
1727 INTEGER,
DIMENSION(3),
INTENT(IN) ::
idx
1729 INTEGER :: iatom, natom
1730 REAL(kind=
dp),
DIMENSION(3, 3) :: tenvec
1733 natom =
SIZE(coord, 2)
1734 tenvec = transpose(sym%tenvec)
1736 coord(
idx, iatom) = matmul(tenvec, coord(:, iatom))
1740 sym%inptostd(
idx, :) = tenvec
1742 END SUBROUTINE tracar
1751 FUNCTION dist(a, b)
RESULT(d)
1752 REAL(kind=
dp),
DIMENSION(3),
INTENT(IN) :: a, b
1755 d = sqrt(sum((a - b)**2))
1769 SUBROUTINE write_coordinates(coord, atype, element, z, weight, iw)
1770 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(in) :: coord
1771 INTEGER,
DIMENSION(:),
INTENT(in) :: atype
1772 CHARACTER(LEN=*),
DIMENSION(:),
INTENT(in) :: element
1773 INTEGER,
DIMENSION(:),
INTENT(in) :: z
1774 REAL(kind=
dp),
DIMENSION(:),
INTENT(in) :: weight
1775 INTEGER,
INTENT(IN) :: iw
1777 INTEGER :: iatom, natom
1780 natom =
SIZE(coord, 2)
1781 WRITE (unit=iw, fmt=
"(T2,A)") &
1783 "MOLSYM| Atomic coordinates of the standard orientation in BOHR"
1784 WRITE (unit=iw, fmt=
"(T2,A,T37,A,T51,A,T65,A,T77,A)") &
1785 "MOLSYM| Atom Kind Element",
"X",
"Y",
"Z",
"Mass"
1787 WRITE (unit=iw, fmt=
"(T2,A,I7,1X,I4,1X,A2,1X,I4,3(1X,F13.8),1X,F9.4)") &
1788 "MOLSYM|", iatom, atype(iatom), adjustl(element(iatom)), z(iatom), &
1789 coord(1:3, iatom), weight(iatom)
1791 WRITE (unit=iw, fmt=
"(T2,A)") &
1793 "MOLSYM| Atomic coordinates of the standard orientation in ANGSTROM"
1794 WRITE (unit=iw, fmt=
"(T2,A,T37,A,T51,A,T65,A,T77,A)") &
1795 "MOLSYM| Atom Kind Element",
"X",
"Y",
"Z",
"Mass"
1797 WRITE (unit=iw, fmt=
"(T2,A,I7,1X,I4,1X,A2,1X,I4,3(1X,F13.8),1X,F9.4)") &
1798 "MOLSYM|", iatom, atype(iatom), adjustl(element(iatom)), z(iatom), &
1799 coord(1:3, iatom)*
angstrom, weight(iatom)
1803 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
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, 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.