LCOV - code coverage report
Current view: top level - src - molsym.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:262480d) Lines: 636 657 96.8 %
Date: 2024-11-22 07:00:40 Functions: 30 31 96.8 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief Molecular symmetry routines
      10             : !> \par History
      11             : !>      2008 adapted from older routines by Matthias Krack
      12             : !> \author jgh
      13             : ! **************************************************************************************************
      14             : MODULE molsym
      15             : 
      16             :    USE kinds,                           ONLY: dp
      17             :    USE mathconstants,                   ONLY: pi
      18             :    USE mathlib,                         ONLY: angle,&
      19             :                                               build_rotmat,&
      20             :                                               jacobi,&
      21             :                                               reflect_vector,&
      22             :                                               rotate_vector,&
      23             :                                               unit_matrix,&
      24             :                                               vector_product
      25             :    USE physcon,                         ONLY: angstrom
      26             :    USE util,                            ONLY: sort
      27             : #include "./base/base_uses.f90"
      28             : 
      29             :    IMPLICIT NONE
      30             :    PRIVATE
      31             : 
      32             :    LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .TRUE.
      33             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'molsym'
      34             : 
      35             :    INTEGER, PARAMETER :: maxcn = 20, &
      36             :                          maxsec = maxcn + 1, &
      37             :                          maxses = 2*maxcn + 1, &
      38             :                          maxsig = maxcn + 1, &
      39             :                          maxsn = 2*maxcn
      40             : 
      41             :    PUBLIC :: molsym_type
      42             :    PUBLIC :: release_molsym, molecular_symmetry, print_symmetry
      43             : 
      44             : ! **************************************************************************************************
      45             : !> \brief Container for information about molecular symmetry
      46             : !> \param ain               : Atomic identification numbers (symmetry code).
      47             : !> \param aw                : Atomic weights for the symmetry analysis.
      48             : !> \param eps_geo           : Accuracy requested for analysis
      49             : !> \param inptostd          : Transformation matrix for input to standard orientation.
      50             : !> \param point_group_symbol: Point group symbol.
      51             : !> \param rotmat            : Rotation matrix.
      52             : !> \param sec               : List of C axes
      53             : !>                            (sec(:,i,j) => x,y,z of the ith j-fold C axis).
      54             : !> \param ses               : List of S axes
      55             : !>                            (ses(:,i,j) => x,y,z of the ith j-fold S axis).
      56             : !> \param sig               : List of mirror planes
      57             : !>                            (sig(:,i) => x,y,z of the ith mirror plane).
      58             : !> \param center_of_mass    : Shift vector for the center of mass.
      59             : !> \param tenmat            : Molecular tensor of inertia.
      60             : !> \param tenval            : Eigenvalues of the molecular tensor of inertia.
      61             : !> \param tenvec            : Eigenvectors of the molecular tensor of inertia.
      62             : !> \param group_of          : Group of equivalent atom.
      63             : !> \param llequatom         : Lower limit of a group in symequ_list.
      64             : !> \param ncn               : Degree of the C axis with highest degree.
      65             : !> \param ndod              : Number of substituted dodecahedral angles.
      66             : !> \param nequatom          : Number of equivalent atoms.
      67             : !> \param ngroup            : Number of groups of equivalent atoms.
      68             : !> \param nico              : Number of substituted icosahedral angles.
      69             : !> \param nlin              : Number of substituted angles of 180 degrees.
      70             : !> \param nsec              : Number of C axes.
      71             : !> \param nses              : Number of S axes.
      72             : !> \param nsig              : Number of mirror planes.
      73             : !> \param nsn               : Degree of the S axis with highest degree.
      74             : !> \param ntet              : Number of substituted tetrahedral angles.
      75             : !> \param point_group_order : Group order.
      76             : !> \param symequ_list       : List of all atoms ordered in groups of equivalent atoms.
      77             : !> \param ulequatom         : Upper limit of a group in symequ_list.
      78             : !> \param cubic : .TRUE., if a cubic point group was found (T,Th,Td,O,Oh,I,Ih).
      79             : !> \param dgroup: .TRUE., if a point group of D symmetry was found (Dn,Dnh,Dnd)
      80             : !> \param igroup: .TRUE., if a point group of icosahedral symmetry was found (I,Ih).
      81             : !> \param invers: .TRUE., if the molecule has a center of inversion.
      82             : !> \param linear: .TRUE., if the molecule is linear.
      83             : !> \param maxis : .TRUE., if the molecule has a main axis.
      84             : !> \param ogroup: .TRUE., if a point group of octahedral symmetry was found (O,Oh)
      85             : !> \param sgroup: .TRUE., if a point group of S symmetry was found.
      86             : !> \param sigmad: .TRUE., if there is a sigma_d mirror plane.
      87             : !> \param sigmah: .TRUE., if there is a sigma_h mirror plane.
      88             : !> \param sigmav: .TRUE., if there is a sigma_v mirror plane.
      89             : !> \param tgroup: .TRUE., if a point group of tetrahedral symmetry was found (T,Th,Td).
      90             : !> \par History
      91             : !>      05.2008 created
      92             : !> \author jgh
      93             : ! **************************************************************************************************
      94             :    TYPE molsym_type
      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()
     115             :    END TYPE molsym_type
     116             : 
     117             : ! **************************************************************************************************
     118             : 
     119             : CONTAINS
     120             : 
     121             : ! **************************************************************************************************
     122             : !> \brief  Create an object of molsym type
     123             : !> \param sym ...
     124             : !> \param natoms ...
     125             : !> \author jgh
     126             : ! **************************************************************************************************
     127          58 :    SUBROUTINE create_molsym(sym, natoms)
     128             :       TYPE(molsym_type), POINTER                         :: sym
     129             :       INTEGER, INTENT(IN)                                :: natoms
     130             : 
     131          58 :       IF (ASSOCIATED(sym)) CALL release_molsym(sym)
     132             : 
     133      479718 :       ALLOCATE (sym)
     134             : 
     135             :       ALLOCATE (sym%ain(natoms), sym%aw(natoms), sym%group_of(natoms), sym%llequatom(natoms), &
     136         870 :                 sym%nequatom(natoms), sym%symequ_list(natoms), sym%ulequatom(natoms))
     137             : 
     138          58 :    END SUBROUTINE create_molsym
     139             : 
     140             : ! **************************************************************************************************
     141             : !> \brief  release an object of molsym type
     142             : !> \param sym ...
     143             : !> \author jgh
     144             : ! **************************************************************************************************
     145          58 :    SUBROUTINE release_molsym(sym)
     146             :       TYPE(molsym_type), POINTER                         :: sym
     147             : 
     148          58 :       CPASSERT(ASSOCIATED(sym))
     149             : 
     150          58 :       IF (ASSOCIATED(sym%aw)) THEN
     151          58 :          DEALLOCATE (sym%aw)
     152             :       END IF
     153          58 :       IF (ASSOCIATED(sym%ain)) THEN
     154          58 :          DEALLOCATE (sym%ain)
     155             :       END IF
     156          58 :       IF (ASSOCIATED(sym%group_of)) THEN
     157          58 :          DEALLOCATE (sym%group_of)
     158             :       END IF
     159          58 :       IF (ASSOCIATED(sym%llequatom)) THEN
     160          58 :          DEALLOCATE (sym%llequatom)
     161             :       END IF
     162          58 :       IF (ASSOCIATED(sym%nequatom)) THEN
     163          58 :          DEALLOCATE (sym%nequatom)
     164             :       END IF
     165          58 :       IF (ASSOCIATED(sym%symequ_list)) THEN
     166          58 :          DEALLOCATE (sym%symequ_list)
     167             :       END IF
     168          58 :       IF (ASSOCIATED(sym%ulequatom)) THEN
     169          58 :          DEALLOCATE (sym%ulequatom)
     170             :       END IF
     171             : 
     172          58 :       DEALLOCATE (sym)
     173             : 
     174          58 :    END SUBROUTINE release_molsym
     175             : 
     176             : ! **************************************************************************************************
     177             : 
     178             : ! **************************************************************************************************
     179             : !> \brief Add a new C_n axis to the list sec, but first check, if the
     180             : !>         Cn axis is already in the list.
     181             : !> \param n ...
     182             : !> \param a ...
     183             : !> \param sym ...
     184             : !> \par History
     185             : !>        Creation (19.10.98, Matthias Krack)
     186             : !> \author jgh
     187             : ! **************************************************************************************************
     188        1419 :    SUBROUTINE addsec(n, a, sym)
     189             :       INTEGER, INTENT(IN)                                :: n
     190             :       REAL(dp), DIMENSION(3), INTENT(IN)                 :: a
     191             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     192             : 
     193             :       INTEGER                                            :: isec
     194             :       REAL(dp)                                           :: length_of_a, scapro
     195             :       REAL(dp), DIMENSION(3)                             :: d
     196             : 
     197        1419 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
     198        5676 :       d(:) = a(:)/length_of_a
     199             : 
     200             :       ! Check, if the current Cn axis is already in the list
     201        6384 :       DO isec = 1, sym%nsec(n)
     202        6088 :          scapro = sym%sec(1, isec, n)*d(1) + sym%sec(2, isec, n)*d(2) + sym%sec(3, isec, n)*d(3)
     203        6384 :          IF (ABS(ABS(scapro) - 1.0_dp) < sym%eps_geo) RETURN
     204             :       END DO
     205         296 :       sym%ncn = MAX(sym%ncn, n)
     206             : 
     207             :       ! Add the new Cn axis to the list sec
     208         296 :       CPASSERT(sym%nsec(n) < maxsec)
     209         296 :       sym%nsec(1) = sym%nsec(1) + 1
     210         296 :       sym%nsec(n) = sym%nsec(n) + 1
     211        1184 :       sym%sec(:, sym%nsec(n), n) = d(:)
     212             : 
     213             :    END SUBROUTINE addsec
     214             : 
     215             : ! **************************************************************************************************
     216             : !> \brief  Add a new Sn axis to the list ses, but first check, if the
     217             : !>         Sn axis is already in the list.
     218             : !> \param n ...
     219             : !> \param a ...
     220             : !> \param sym ...
     221             : !> \par History
     222             : !>        Creation (19.10.98, Matthias Krack)
     223             : !> \author jgh
     224             : ! **************************************************************************************************
     225         284 :    SUBROUTINE addses(n, a, sym)
     226             :       INTEGER, INTENT(IN)                                :: n
     227             :       REAL(dp), DIMENSION(3), INTENT(IN)                 :: a
     228             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     229             : 
     230             :       INTEGER                                            :: ises
     231             :       REAL(dp)                                           :: length_of_a, scapro
     232             :       REAL(dp), DIMENSION(3)                             :: d
     233             : 
     234         284 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
     235        1136 :       d(:) = a(:)/length_of_a
     236             : 
     237             :       ! Check, if the current Sn axis is already in the list
     238        1051 :       DO ises = 1, sym%nses(n)
     239         888 :          scapro = sym%ses(1, ises, n)*d(1) + sym%ses(2, ises, n)*d(2) + sym%ses(3, ises, n)*d(3)
     240        1051 :          IF (ABS(ABS(scapro) - 1.0_dp) < sym%eps_geo) RETURN
     241             :       END DO
     242         163 :       sym%nsn = MAX(sym%nsn, n)
     243             : 
     244             :       ! Add the new Sn axis to the list ses
     245         163 :       CPASSERT(sym%nses(n) < maxses)
     246         163 :       sym%nses(1) = sym%nses(1) + 1
     247         163 :       sym%nses(n) = sym%nses(n) + 1
     248         652 :       sym%ses(:, sym%nses(n), n) = d(:)
     249             : 
     250             :    END SUBROUTINE addses
     251             : 
     252             : ! **************************************************************************************************
     253             : !> \brief  Add a new mirror plane to the list sig, but first check, if the
     254             : !>         normal vector of the mirror plane is already in the list.
     255             : !> \param a ...
     256             : !> \param sym ...
     257             : !> \par History
     258             : !>        Creation (19.10.98, Matthias Krack)
     259             : !> \author jgh
     260             : ! **************************************************************************************************
     261         597 :    SUBROUTINE addsig(a, sym)
     262             :       REAL(dp), DIMENSION(3), INTENT(IN)                 :: a
     263             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     264             : 
     265             :       INTEGER                                            :: isig
     266             :       REAL(dp)                                           :: length_of_a, scapro
     267             :       REAL(dp), DIMENSION(3)                             :: d
     268             : 
     269         597 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
     270        2388 :       d(:) = a(:)/length_of_a
     271             : 
     272             :       ! Check, if the normal vector of the current mirror plane is already in the list
     273        2426 :       DO isig = 1, sym%nsig
     274        2273 :          scapro = sym%sig(1, isig)*d(1) + sym%sig(2, isig)*d(2) + sym%sig(3, isig)*d(3)
     275        2426 :          IF (ABS(ABS(scapro) - 1.0_dp) < sym%eps_geo) RETURN
     276             :       END DO
     277             : 
     278             :       ! Add the normal vector of the new mirror plane to the list sig
     279         153 :       CPASSERT(sym%nsig < maxsig)
     280         153 :       sym%nsig = sym%nsig + 1
     281         612 :       sym%sig(:, sym%nsig) = d(:)
     282             : 
     283             :    END SUBROUTINE addsig
     284             : 
     285             : ! **************************************************************************************************
     286             : !> \brief  Symmetry handling for only one atom.
     287             : !> \param sym ...
     288             : !> \par History
     289             : !>        Creation (19.10.98, Matthias Krack)
     290             : !> \author jgh
     291             : ! **************************************************************************************************
     292           1 :    SUBROUTINE atomsym(sym)
     293             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     294             : 
     295             : ! Set point group symbol
     296             : 
     297           1 :       sym%point_group_symbol = "R(3)"
     298             : 
     299             :       ! Set variables like D*h
     300           1 :       sym%linear = .TRUE.
     301           1 :       sym%invers = .TRUE.
     302           1 :       sym%dgroup = .TRUE.
     303           1 :       sym%sigmah = .TRUE.
     304             : 
     305           1 :    END SUBROUTINE atomsym
     306             : 
     307             : ! **************************************************************************************************
     308             : !> \brief Search for point groups with AXial SYMmetry (Cn,Cnh,Cnv,Dn,Dnh,Dnd,S2n).
     309             : !> \param coord ...
     310             : !> \param sym ...
     311             : !> \par History
     312             : !>        Creation (19.10.98, Matthias Krack)
     313             : !> \author jgh
     314             : ! **************************************************************************************************
     315          45 :    SUBROUTINE axsym(coord, sym)
     316             :       REAL(Kind=dp), DIMENSION(:, :), INTENT(inout)      :: coord
     317             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     318             : 
     319             :       INTEGER                                            :: iatom, isig, jatom, m, n, natoms, nx, &
     320             :                                                             ny, nz
     321             :       REAL(dp)                                           :: phi
     322             :       REAL(dp), DIMENSION(3)                             :: a, b, d
     323             : 
     324             : ! Find the correct main axis and rotate main axis to z axis
     325             : 
     326          45 :       IF ((sym%ncn == 2) .AND. (sym%nses(2) == 3)) THEN
     327           2 :          IF (sym%nsn == 4) THEN
     328             :             ! Special case: D2d
     329           0 :             phi = angle(sym%ses(:, 1, sym%nsn), sym%z_axis(:))
     330           0 :             d(:) = vector_product(sym%ses(:, 1, sym%nsn), sym%z_axis(:))
     331           0 :             CALL rotate_molecule(phi, d(:), sym, coord)
     332             :          ELSE
     333             :             ! Special cases: D2 and D2h
     334           2 :             phi = 0.5_dp*pi
     335           2 :             nx = naxis(sym%x_axis(:), coord, sym)
     336           2 :             ny = naxis(sym%y_axis(:), coord, sym)
     337           2 :             nz = naxis(sym%z_axis(:), coord, sym)
     338           2 :             IF ((nx > ny) .AND. (nx > nz)) THEN
     339           0 :                CALL rotate_molecule(-phi, sym%y_axis(:), sym, coord)
     340           2 :             ELSE IF ((ny > nz) .AND. (ny > nx)) THEN
     341           0 :                CALL rotate_molecule(phi, sym%x_axis(:), sym, coord)
     342             :             END IF
     343             :          END IF
     344             :       ELSE
     345          43 :          phi = angle(sym%sec(:, 1, sym%ncn), sym%z_axis(:))
     346          43 :          d(:) = vector_product(sym%sec(:, 1, sym%ncn), sym%z_axis(:))
     347          43 :          CALL rotate_molecule(phi, d(:), sym, coord)
     348             :       END IF
     349             : 
     350             :       ! Search for C2 axes perpendicular to the main axis
     351             :       ! Loop over all pairs of atoms (except on the z axis)
     352          45 :       natoms = SIZE(coord, 2)
     353         459 :       DO iatom = 1, natoms
     354        1656 :          a(:) = coord(:, iatom)
     355         459 :          IF ((ABS(a(1)) > sym%eps_geo) .OR. (ABS(a(2)) > sym%eps_geo)) THEN
     356         388 :             a(3) = 0.0_dp
     357         388 :             IF (caxis(2, a(:), sym, coord)) CALL addsec(2, a(:), sym)
     358         388 :             d(:) = vector_product(a(:), sym%z_axis(:))
     359         388 :             IF (sigma(d(:), sym, coord)) CALL addsig(d(:), sym)
     360        2283 :             DO jatom = iatom + 1, natoms
     361        7580 :                b(:) = coord(:, jatom)
     362        2309 :                IF ((ABS(b(1)) > sym%eps_geo) .OR. (ABS(b(2)) > sym%eps_geo)) THEN
     363        1871 :                   b(3) = 0.0_dp
     364        1871 :                   phi = 0.5_dp*angle(a(:), b(:))
     365        1871 :                   d(:) = vector_product(a(:), b(:))
     366        1871 :                   b(:) = rotate_vector(a(:), phi, d(:))
     367        1871 :                   IF (caxis(2, b(:), sym, coord)) CALL addsec(2, b(:), sym)
     368        1871 :                   d(:) = vector_product(b(:), sym%z_axis)
     369        1895 :                   IF (sigma(d(:), sym, coord)) CALL addsig(d(:), sym)
     370             :                END IF
     371             :             END DO
     372             :          END IF
     373             :       END DO
     374             : 
     375             :       ! Check the xy plane for mirror plane
     376          45 :       IF (sigma(sym%z_axis(:), sym, coord)) THEN
     377          14 :          CALL addsig(sym%z_axis(:), sym)
     378          14 :          sym%sigmah = .TRUE.
     379             :       END IF
     380             : 
     381             :       ! Set logical variables for point group determination ***
     382          45 :       IF (sym%nsec(2) > 1) THEN
     383          21 :          sym%dgroup = .TRUE.
     384          21 :          IF ((.NOT. sym%sigmah) .AND. (sym%nsig > 0)) sym%sigmad = .TRUE.
     385             :       ELSE
     386          24 :          IF ((.NOT. sym%sigmah) .AND. (sym%nsig > 0)) sym%sigmav = .TRUE.
     387             :          ! Cnh groups with n>3 were wrong
     388             :          ! IF (sym%nsn > 3) sym%sgroup = .TRUE.
     389          24 :          IF ((.NOT. sym%sigmah) .AND. (sym%nsn > 3)) sym%sgroup = .TRUE.
     390             :       END IF
     391             : 
     392             :       ! Rotate to standard orientation
     393          45 :       n = 0
     394          45 :       m = 0
     395          45 :       IF ((sym%dgroup .AND. sym%sigmah) .OR. (sym%dgroup .AND. sym%sigmad) .OR. sym%sigmav) THEN
     396             :          ! Dnh, Dnd or Cnv
     397         133 :          DO isig = 1, sym%nsig
     398         133 :             IF (ABS(sym%sig(3, isig)) < sym%eps_geo) THEN
     399         105 :                n = nsigma(sym%sig(:, isig), sym, coord)
     400         105 :                IF (n > m) THEN
     401          21 :                   m = n
     402          84 :                   a(:) = sym%sig(:, isig)
     403             :                END IF
     404             :             END IF
     405             :          END DO
     406          21 :          IF (m > 0) THEN
     407             :             ! Check for special case: C2v with all atoms in a plane
     408          21 :             IF (sym%sigmav .AND. (m == natoms)) THEN
     409           1 :                phi = angle(a(:), sym%x_axis(:))
     410           1 :                d(:) = vector_product(a(:), sym%x_axis(:))
     411             :             ELSE
     412          20 :                phi = angle(a(:), sym%y_axis(:))
     413          20 :                d(:) = vector_product(a(:), sym%y_axis(:))
     414             :             END IF
     415          21 :             CALL rotate_molecule(phi, d(:), sym, coord)
     416             :          END IF
     417          24 :       ELSE IF (sym%sigmah) THEN
     418             :          ! Cnh
     419          81 :          DO iatom = 1, natoms
     420          81 :             IF (ABS(coord(3, iatom)) < sym%eps_geo) THEN
     421          70 :                n = naxis(coord(:, iatom), coord, sym)
     422          70 :                IF (n > m) THEN
     423          28 :                   m = n
     424          28 :                   a(:) = coord(:, iatom)
     425             :                END IF
     426             :             END IF
     427             :          END DO
     428           7 :          IF (m > 0) THEN
     429           7 :             phi = angle(a(:), sym%x_axis(:))
     430           7 :             d(:) = vector_product(a(:), sym%x_axis(:))
     431           7 :             CALL rotate_molecule(phi, d(:), sym, coord)
     432             :          END IF
     433             :          ! No action for Dn, Cn or S2n ***
     434             :       END IF
     435             : 
     436          45 :    END SUBROUTINE axsym
     437             : 
     438             : ! **************************************************************************************************
     439             : !> \brief Generate a symmetry list to identify equivalent atoms.
     440             : !> \param sym ...
     441             : !> \param coord ...
     442             : !> \par History
     443             : !>        Creation (19.10.98, Matthias Krack)
     444             : !> \author jgh
     445             : ! **************************************************************************************************
     446          58 :    SUBROUTINE build_symequ_list(sym, coord)
     447             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     448             :       REAL(Kind=dp), DIMENSION(:, :), INTENT(inout)      :: coord
     449             : 
     450             :       INTEGER                                            :: i, iatom, icn, iequatom, incr, isec, &
     451             :                                                             ises, isig, isn, jatom, jcn, jsn, &
     452             :                                                             natoms
     453             :       REAL(KIND=dp)                                      :: phi
     454             :       REAL(KIND=dp), DIMENSION(3)                        :: a
     455             : 
     456          58 :       natoms = SIZE(coord, 2)
     457             : 
     458          58 :       IF (sym%sigmah) THEN
     459             :          incr = 1
     460             :       ELSE
     461          41 :          incr = 2
     462             :       END IF
     463             : 
     464             :       ! Initialize the atom and the group counter
     465          58 :       iatom = 1
     466          58 :       sym%ngroup = 1
     467             : 
     468             :       loop: DO
     469             : 
     470             :          ! Loop over all mirror planes
     471         332 :          DO isig = 1, sym%nsig
     472         226 :             a(:) = reflect_vector(coord(:, iatom), sym%sig(:, isig))
     473         226 :             iequatom = equatom(iatom, a(:), sym, coord)
     474         332 :             IF ((iequatom > 0) .AND. (.NOT. in_symequ_list(iequatom, sym))) THEN
     475         111 :                sym%ulequatom(sym%ngroup) = sym%ulequatom(sym%ngroup) + 1
     476         111 :                sym%symequ_list(sym%ulequatom(sym%ngroup)) = iequatom
     477         111 :                sym%nequatom(sym%ngroup) = sym%nequatom(sym%ngroup) + 1
     478             :             END IF
     479             :          END DO
     480             : 
     481             :          ! Loop over all Cn axes
     482         445 :          DO icn = 2, sym%ncn
     483         829 :             DO isec = 1, sym%nsec(icn)
     484        1465 :                DO jcn = 1, icn - 1
     485        1126 :                   IF (newse(jcn, icn)) THEN
     486         644 :                      phi = 2.0_dp*pi*REAL(jcn, KIND=dp)/REAL(icn, KIND=dp)
     487         644 :                      a(:) = rotate_vector(coord(:, iatom), phi, sym%sec(:, isec, icn))
     488         644 :                      iequatom = equatom(iatom, a(:), sym, coord)
     489         644 :                      IF ((iequatom > 0) .AND. (.NOT. in_symequ_list(iequatom, sym))) THEN
     490         328 :                         sym%ulequatom(sym%ngroup) = sym%ulequatom(sym%ngroup) + 1
     491         328 :                         sym%symequ_list(sym%ulequatom(sym%ngroup)) = iequatom
     492         328 :                         sym%nequatom(sym%ngroup) = sym%nequatom(sym%ngroup) + 1
     493             :                      END IF
     494             :                   END IF
     495             :                END DO
     496             :             END DO
     497             :          END DO
     498             : 
     499             :          ! Loop over all Sn axes
     500         339 :          DO isn = 2, sym%nsn
     501         442 :             DO ises = 1, sym%nses(isn)
     502         643 :                DO jsn = 1, isn - 1, incr
     503         410 :                   IF (newse(jsn, isn)) THEN
     504         243 :                      phi = 2.0_dp*pi*REAL(jsn, KIND=dp)/REAL(isn, KIND=dp)
     505         243 :                      a(:) = rotate_vector(coord(:, iatom), phi, sym%ses(:, ises, isn))
     506         972 :                      a(:) = reflect_vector(a(:), sym%ses(:, ises, isn))
     507         243 :                      iequatom = equatom(iatom, a(:), sym, coord)
     508         243 :                      IF ((iequatom > 0) .AND. (.NOT. in_symequ_list(iequatom, sym))) THEN
     509          30 :                         sym%ulequatom(sym%ngroup) = sym%ulequatom(sym%ngroup) + 1
     510          30 :                         sym%symequ_list(sym%ulequatom(sym%ngroup)) = iequatom
     511          30 :                         sym%nequatom(sym%ngroup) = sym%nequatom(sym%ngroup) + 1
     512             :                      END IF
     513             :                   END IF
     514             :                END DO
     515             :             END DO
     516             :          END DO
     517             : 
     518             :          ! Exit loop, if all atoms are in the list
     519         106 :          IF (sym%ulequatom(sym%ngroup) == natoms) EXIT
     520             : 
     521             :          ! Search for the next atom which is not in the list yet
     522         156 :          DO jatom = 2, natoms
     523          98 :             IF (.NOT. in_symequ_list(jatom, sym)) THEN
     524          48 :                iatom = jatom
     525          48 :                sym%ngroup = sym%ngroup + 1
     526          48 :                sym%llequatom(sym%ngroup) = sym%ulequatom(sym%ngroup - 1) + 1
     527          48 :                sym%ulequatom(sym%ngroup) = sym%llequatom(sym%ngroup)
     528          48 :                sym%symequ_list(sym%llequatom(sym%ngroup)) = iatom
     529          48 :                CYCLE loop
     530             :             END IF
     531             :          END DO
     532             : 
     533             :       END DO loop
     534             : 
     535             :       ! Generate list vector group_of
     536         164 :       DO i = 1, sym%ngroup
     537         739 :          DO iequatom = sym%llequatom(i), sym%ulequatom(i)
     538         681 :             sym%group_of(sym%symequ_list(iequatom)) = i
     539             :          END DO
     540             :       END DO
     541             : 
     542          58 :    END SUBROUTINE build_symequ_list
     543             : 
     544             : ! **************************************************************************************************
     545             : !> \brief Rotate the molecule about a n-fold axis defined by the vector a
     546             : !>        using a rotation angle of 2*pi/n. Check, if the axis is a Cn axis.
     547             : !> \param n ...
     548             : !> \param a ...
     549             : !> \param sym ...
     550             : !> \param coord ...
     551             : !> \return ...
     552             : !> \par History
     553             : !>        Creation (19.10.98, Matthias Krack)
     554             : !> \author jgh
     555             : ! **************************************************************************************************
     556        5543 :    FUNCTION caxis(n, a, sym, coord)
     557             :       INTEGER, INTENT(IN)                                :: n
     558             :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a
     559             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     560             :       REAL(Kind=dp), DIMENSION(:, :), INTENT(inout)      :: coord
     561             :       LOGICAL                                            :: caxis
     562             : 
     563             :       INTEGER                                            :: iatom, natoms
     564             :       REAL(KIND=dp)                                      :: length_of_a, phi
     565             :       REAL(KIND=dp), DIMENSION(3)                        :: b
     566             : 
     567        5543 :       caxis = .FALSE.
     568        5543 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
     569             : 
     570             :       ! Check the length of the axis vector a
     571        5543 :       natoms = SIZE(coord, 2)
     572        5543 :       IF (length_of_a > sym%eps_geo) THEN
     573             :          ! Calculate the rotation angle phi and build up the rotation matrix rotmat
     574        5409 :          phi = 2.0_dp*pi/REAL(n, dp)
     575        5409 :          CALL build_rotmat(phi, a(:), sym%rotmat(:, :))
     576             :          ! Check all atoms
     577       18830 :          DO iatom = 1, natoms
     578             :             ! Rotate the actual atom by 2*pi/n about a
     579      234507 :             b(:) = MATMUL(sym%rotmat(:, :), coord(:, iatom))
     580             :             ! Check if the coordinates of the rotated atom
     581             :             ! are in the coordinate set of the molecule
     582       18830 :             IF (equatom(iatom, b(:), sym, coord) == 0) RETURN
     583             :          END DO
     584             :          ! The axis defined by vector a is a Cn axis, thus return with caxis = .TRUE.
     585             :          caxis = .TRUE.
     586             :       END IF
     587             : 
     588             :    END FUNCTION caxis
     589             : 
     590             : ! **************************************************************************************************
     591             : !> \brief Check for a point group of cubic symmetry (I,Ih,O,Oh,T,Th,Td).
     592             : !> \param sym ...
     593             : !> \param coord ...
     594             : !> \param failed ...
     595             : !> \par History
     596             : !>        Creation (19.10.98, Matthias Krack)
     597             : !> \author jgh
     598             : ! **************************************************************************************************
     599           7 :    SUBROUTINE cubsym(sym, coord, failed)
     600             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     601             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
     602             :       LOGICAL, INTENT(INOUT)                             :: failed
     603             : 
     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
     608             : 
     609             :       ! Angle between two adjacent atoms of the dodecahedron => <(C3,C3)
     610           7 :       phidd = ATAN(0.4_dp*SQRT(5.0_dp))
     611             :       ! Angle between two adjacent atoms of the icosahedron and the dodecahedron => <(C5,C3)
     612           7 :       phidi = ATAN(3.0_dp - SQRT(5.0_dp))
     613             :       ! Angle between two adjacent atoms of the icosahedron <(C5,C5)
     614           7 :       phiii = ATAN(2.0_dp)
     615             : 
     616             :       ! Find a pair of C3 axes
     617           7 :       natoms = SIZE(coord, 2)
     618         146 :       DO iatom = 1, natoms
     619             :          ! Check all atomic vectors for C3 axis
     620         146 :          IF (caxis(3, coord(:, iatom), sym, coord)) THEN
     621           2 :             CALL addsec(3, coord(:, iatom), sym)
     622           2 :             IF (sym%nsec(3) > 1) EXIT
     623             :          END IF
     624             :       END DO
     625             : 
     626             :       ! Check the center of mass vector of a triangle
     627             :       ! defined by three atomic vectors for C3 axis
     628           7 :       IF (sym%nsec(3) < 2) THEN
     629             : 
     630           6 :          loop: DO iatom = 1, natoms
     631          24 :             a(:) = coord(:, iatom)
     632          24 :             DO jatom = iatom + 1, natoms
     633         734 :                DO katom = jatom + 1, natoms
     634             :                   IF ((ABS(dist(coord(:, iatom), coord(:, jatom)) &
     635         716 :                            - dist(coord(:, iatom), coord(:, katom))) < sym%eps_geo) .AND. &
     636             :                       (ABS(dist(coord(:, iatom), coord(:, jatom)) &
     637          18 :                            - dist(coord(:, jatom), coord(:, katom))) < sym%eps_geo)) THEN
     638          48 :                      b(:) = a(:) + coord(:, jatom) + coord(:, katom)
     639          12 :                      IF (caxis(3, b(:), sym, coord)) THEN
     640          12 :                         CALL addsec(3, b(:), sym)
     641          12 :                         IF (sym%nsec(3) > 1) EXIT loop
     642             :                      END IF
     643             :                   END IF
     644             :                END DO
     645             :             END DO
     646             :          END DO loop
     647             : 
     648             :       END IF
     649             : 
     650             :       ! Return after unsuccessful search for a pair of C3 axes
     651           7 :       IF (sym%nsec(3) < 2) THEN
     652           0 :          failed = .TRUE.
     653           0 :          RETURN
     654             :       END IF
     655             : 
     656             :       ! Generate the remaining C3 axes
     657          16 :       nc3 = 0
     658             :       DO
     659          16 :          nc3 = sym%nsec(3)
     660          82 :          DO ic3 = 1, nc3
     661         264 :             a(:) = sym%sec(:, ic3, 3)
     662         462 :             DO jc3 = 1, nc3
     663         446 :                IF (ic3 /= jc3) THEN
     664        1256 :                   d(:) = sym%sec(:, jc3, 3)
     665         942 :                   DO i = 1, 2
     666         628 :                      phi1 = 2.0_dp*REAL(i, KIND=dp)*pi/3.0_dp
     667         628 :                      b(:) = rotate_vector(a(:), phi1, d(:))
     668         942 :                      CALL addsec(3, b(:), sym)
     669             :                   END DO
     670             :                END IF
     671             :             END DO
     672             :          END DO
     673             : 
     674          16 :          IF (sym%nsec(3) > nc3) THEN
     675             :             ! New C3 axes were found
     676             :             CYCLE
     677             :          ELSE
     678             :             ! In the case of I or Ih there have to be more C3 axes
     679           7 :             IF (sym%nsec(3) == 4) THEN
     680          20 :                a(:) = sym%sec(:, 1, 3)
     681          20 :                b(:) = sym%sec(:, 2, 3)
     682           5 :                phi1 = 0.5_dp*angle(a(:), b(:))
     683           5 :                IF (phi1 < 0.5_dp*pi) phi1 = phi1 - 0.5_dp*pi
     684           5 :                d(:) = vector_product(a(:), b(:))
     685           5 :                b(:) = rotate_vector(a(:), phi1, d(:))
     686          20 :                c(:) = sym%sec(:, 3, 3)
     687           5 :                phi1 = 0.5_dp*angle(a(:), c(:))
     688           5 :                IF (phi1 < 0.5_dp*pi) phi1 = phi1 - 0.5_dp*pi
     689           5 :                d(:) = vector_product(a(:), c(:))
     690           5 :                c(:) = rotate_vector(a(:), phi1, d(:))
     691           5 :                d(:) = vector_product(b(:), c(:))
     692           5 :                phi1 = 0.5_dp*phidd
     693           5 :                a(:) = rotate_vector(b(:), phi1, d(:))
     694           5 :                IF (caxis(3, a(:), sym, coord)) THEN
     695           0 :                   CALL addsec(3, a(:), sym)
     696             :                ELSE
     697           5 :                   phi2 = 0.5_dp*pi - phi1
     698           5 :                   a(:) = rotate_vector(b(:), phi2, d(:))
     699           5 :                   IF (caxis(3, a(:), sym, coord)) CALL addsec(3, a(:), sym)
     700             :                END IF
     701             : 
     702           5 :                IF (sym%nsec(3) > 4) CYCLE
     703             : 
     704           2 :             ELSE IF (sym%nsec(3) /= 10) THEN
     705             : 
     706             :                !  C3 axes found, but only 4 or 10 are possible
     707           0 :                failed = .TRUE.
     708           0 :                RETURN
     709             : 
     710             :             END IF
     711             : 
     712             :             ! Exit loop, if 4 or 10 C3 axes were found
     713           9 :             EXIT
     714             : 
     715             :          END IF
     716             : 
     717             :       END DO
     718             : 
     719             :       ! Loop over all pairs of C3 axes to find all C5, C4 and C2 axes
     720          47 :       DO isec = 1, sym%nsec(3)
     721             : 
     722         160 :          a(:) = sym%sec(:, isec, 3)
     723         167 :          DO jsec = isec + 1, sym%nsec(3)
     724         120 :             phi1 = 0.5_dp*angle(a(:), sym%sec(:, jsec, 3))
     725         120 :             d(:) = vector_product(a(:), sym%sec(:, jsec, 3))
     726             : 
     727             :             ! Check for C5 axis (I,Ih)
     728         120 :             IF (sym%nsec(3) == 10) THEN
     729          90 :                b(:) = rotate_vector(a(:), phidi, d(:))
     730          90 :                IF (caxis(5, b(:), sym, coord)) THEN
     731          14 :                   CALL addsec(5, b(:), sym)
     732          14 :                   phi1 = phidi + phiii
     733          14 :                   b(:) = rotate_vector(a(:), phi1, d(:))
     734          14 :                   IF (caxis(5, b(:), sym, coord)) CALL addsec(5, b(:), sym)
     735             :                END IF
     736             :             END IF
     737             : 
     738             :             ! Check for C4 (O,Oh), C2 and S2 (center of inversion) axis
     739         360 :             DO i = 0, 1
     740         240 :                phi2 = phi1 - 0.5_dp*REAL(i, KIND=dp)*pi
     741         240 :                b(:) = rotate_vector(a(:), phi2, d(:))
     742         240 :                IF (caxis(2, b(:), sym, coord)) THEN
     743         134 :                   CALL addsec(2, b(:), sym)
     744         134 :                   IF (sym%nsec(3) == 4) THEN
     745          42 :                      IF (caxis(4, b(:), sym, coord)) CALL addsec(4, b(:), sym)
     746             :                   END IF
     747         134 :                   IF (saxis(2, b(:), sym, coord)) THEN
     748          64 :                      CALL addses(2, b(:), sym)
     749          64 :                      sym%invers = .TRUE.
     750             :                   END IF
     751             :                END IF
     752         360 :                IF (sigma(b(:), sym, coord)) CALL addsig(b(:), sym)
     753             :             END DO
     754             : 
     755             :             ! Check for mirror plane
     756         160 :             IF (sigma(d(:), sym, coord)) CALL addsig(d(:), sym)
     757             : 
     758             :          END DO
     759             : 
     760             :       END DO
     761             : 
     762             :       ! Set the logical variables for point group determination
     763           7 :       iax = sym%ncn
     764             : 
     765           7 :       IF ((sym%ncn == 5) .AND. (sym%nsec(5) > 1)) THEN
     766           2 :          sym%igroup = .TRUE.
     767           5 :       ELSE IF ((sym%ncn == 4) .AND. (sym%nsec(4) > 1)) THEN
     768           2 :          sym%ogroup = .TRUE.
     769           3 :       ELSE IF ((sym%ncn == 3) .AND. (sym%nsec(3) > 1)) THEN
     770           3 :          sym%tgroup = .TRUE.
     771           3 :          IF ((.NOT. sym%invers) .AND. (sym%nsig > 0)) sym%sigmad = .TRUE.
     772             :          iax = 2
     773             :       ELSE
     774             :          ! No main axis found
     775           0 :          failed = .TRUE.
     776           0 :          RETURN
     777             :       END IF
     778             : 
     779             :       ! Rotate molecule to standard orientation
     780           7 :       phi1 = angle(sym%sec(:, 1, iax), sym%z_axis(:))
     781           7 :       d(:) = vector_product(sym%sec(:, 1, iax), sym%z_axis(:))
     782           7 :       CALL rotate_molecule(phi1, d(:), sym, coord)
     783             : 
     784          28 :       a(:) = sym%sec(:, 2, iax)
     785           7 :       a(3) = 0.0_dp
     786           7 :       phi2 = angle(a(:), sym%x_axis(:))
     787           7 :       d(:) = vector_product(a(:), sym%x_axis(:))
     788           7 :       CALL rotate_molecule(phi2, d(:), sym, coord)
     789             : 
     790             :    END SUBROUTINE cubsym
     791             : 
     792             : ! **************************************************************************************************
     793             : !> \brief The number of a equivalent atom to atoma is returned. If there
     794             : !>        is no equivalent atom, then zero is returned. The cartesian
     795             : !>        coordinates of the equivalent atom are defined by the vector a.
     796             : !> \param atoma ...
     797             : !> \param a ...
     798             : !> \param sym ...
     799             : !> \param coord ...
     800             : !> \return ...
     801             : !> \par History
     802             : !>        Creation (19.10.98, Matthias Krack)
     803             : !> \author jgh
     804             : ! **************************************************************************************************
     805       38170 :    FUNCTION equatom(atoma, a, sym, coord)
     806             :       INTEGER, INTENT(IN)                                :: atoma
     807             :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a
     808             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     809             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
     810             :       INTEGER                                            :: equatom
     811             : 
     812             :       INTEGER                                            :: iatom, natoms
     813             : 
     814       38170 :       equatom = 0
     815       38170 :       natoms = SIZE(coord, 2)
     816      413408 :       DO iatom = 1, natoms
     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      400638 :              (ABS(a(2) - coord(2, iatom)) < sym%eps_geo) .AND. &
     820       12770 :              (ABS(a(3) - coord(3, iatom)) < sym%eps_geo)) THEN
     821       38170 :             equatom = iatom
     822             :             RETURN
     823             :          END IF
     824             :       END DO
     825             : 
     826             :    END FUNCTION equatom
     827             : 
     828             : ! **************************************************************************************************
     829             : !> \brief Calculate the order of the point group.
     830             : !> \param sym ...
     831             : !> \par History
     832             : !>        Creation (19.10.98, Matthias Krack)
     833             : !> \author jgh
     834             : ! **************************************************************************************************
     835          55 :    SUBROUTINE get_point_group_order(sym)
     836             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     837             : 
     838             :       INTEGER                                            :: icn, incr, isec, ises, isn, jcn, jsn
     839             : 
     840             :       ! Count all symmetry elements of the symmetry group
     841             :       ! First E and all mirror planes
     842          55 :       sym%point_group_order = 1 + sym%nsig
     843             : 
     844             :       ! Loop over all C axes
     845         249 :       DO icn = 2, sym%ncn
     846         545 :          DO isec = 1, sym%nsec(icn)
     847        1021 :             DO jcn = 1, icn - 1
     848         827 :                IF (newse(jcn, icn)) sym%point_group_order = sym%point_group_order + 1
     849             :             END DO
     850             :          END DO
     851             :       END DO
     852             : 
     853             :       ! Loop over all S axes
     854          55 :       IF (sym%sigmah) THEN
     855             :          incr = 1
     856             :       ELSE
     857          40 :          incr = 2
     858             :       END IF
     859             : 
     860         212 :       DO isn = 2, sym%nsn
     861         285 :          DO ises = 1, sym%nses(isn)
     862         452 :             DO jsn = 1, isn - 1, incr
     863         295 :                IF (newse(jsn, isn)) sym%point_group_order = sym%point_group_order + 1
     864             :             END DO
     865             :          END DO
     866             :       END DO
     867             : 
     868          55 :    END SUBROUTINE get_point_group_order
     869             : 
     870             : ! **************************************************************************************************
     871             : !> \brief Get the point group symbol.
     872             : !> \param sym ...
     873             : !> \par History
     874             : !>        Creation (19.10.98, Matthias Krack)
     875             : !> \author jgh
     876             : ! **************************************************************************************************
     877          57 :    SUBROUTINE get_point_group_symbol(sym)
     878             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     879             : 
     880             :       CHARACTER(LEN=4)                                   :: degree
     881             : 
     882          57 :       IF (sym%linear) THEN
     883           2 :          IF (sym%invers) THEN
     884           1 :             sym%point_group_symbol = "D*h"
     885             :          ELSE
     886           1 :             sym%point_group_symbol = "C*v"
     887             :          END IF
     888          55 :       ELSE IF (sym%cubic) THEN
     889           7 :          IF (sym%igroup) THEN
     890           2 :             sym%point_group_symbol = "I"
     891           5 :          ELSE IF (sym%ogroup) THEN
     892           2 :             sym%point_group_symbol = "O"
     893           3 :          ELSE IF (sym%tgroup) THEN
     894           3 :             sym%point_group_symbol = "T"
     895             :          END IF
     896           7 :          IF (sym%invers) THEN
     897           3 :             sym%point_group_symbol = TRIM(sym%point_group_symbol)//"h"
     898           4 :          ELSE IF (sym%sigmad) THEN
     899           1 :             sym%point_group_symbol = TRIM(sym%point_group_symbol)//"d"
     900             :          END IF
     901          48 :       ELSE IF (sym%maxis) THEN
     902          45 :          IF (sym%dgroup) THEN
     903          21 :             WRITE (degree, "(I3)") sym%ncn
     904          21 :             sym%point_group_symbol = "D"//TRIM(ADJUSTL(degree))
     905          21 :             IF (sym%sigmah) THEN
     906           7 :                sym%point_group_symbol = TRIM(sym%point_group_symbol)//"h"
     907          14 :             ELSE IF (sym%sigmad) THEN
     908           7 :                sym%point_group_symbol = TRIM(sym%point_group_symbol)//"d"
     909             :             END IF
     910          24 :          ELSE IF (sym%sgroup) THEN
     911           3 :             WRITE (degree, "(I3)") sym%nsn
     912           3 :             sym%point_group_symbol = "S"//TRIM(ADJUSTL(degree))
     913             :          ELSE
     914          21 :             WRITE (degree, "(I3)") sym%ncn
     915          21 :             sym%point_group_symbol = "C"//TRIM(ADJUSTL(degree))
     916          21 :             IF (sym%sigmah) THEN
     917           7 :                sym%point_group_symbol = TRIM(sym%point_group_symbol)//"h"
     918          14 :             ELSE IF (sym%sigmav) THEN
     919           7 :                sym%point_group_symbol = TRIM(sym%point_group_symbol)//"v"
     920             :             END IF
     921             :          END IF
     922             :       ELSE
     923           3 :          IF (sym%invers) THEN
     924           1 :             sym%point_group_symbol = "Ci"
     925           2 :          ELSE IF (sym%sigmah) THEN
     926           1 :             sym%point_group_symbol = "Cs"
     927             :          ELSE
     928           1 :             sym%point_group_symbol = "C1"
     929             :          END IF
     930             :       END IF
     931             : 
     932          57 :    END SUBROUTINE get_point_group_symbol
     933             : 
     934             : ! **************************************************************************************************
     935             : !> \brief Initialization of the global variables of module symmetry.
     936             : !> \param sym ...
     937             : !> \param atype ...
     938             : !> \param weight ...
     939             : !> \par History
     940             : !>        Creation (19.10.98, Matthias Krack)
     941             : !> \author jgh
     942             : ! **************************************************************************************************
     943          58 :    SUBROUTINE init_symmetry(sym, atype, weight)
     944             :       TYPE(molsym_type), INTENT(inout)                   :: sym
     945             :       INTEGER, DIMENSION(:), INTENT(IN)                  :: atype
     946             :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: weight
     947             : 
     948             :       INTEGER                                            :: i, iatom, natoms
     949             : 
     950             :       ! Define the Cartesian axis vectors
     951         232 :       sym%x_axis = (/1.0_dp, 0.0_dp, 0.0_dp/)
     952         232 :       sym%y_axis = (/0.0_dp, 1.0_dp, 0.0_dp/)
     953         232 :       sym%z_axis = (/0.0_dp, 0.0_dp, 1.0_dp/)
     954             : 
     955             :       ! Initialize lists for symmetry elements
     956       93728 :       sym%sec(:, :, :) = 0.0_dp
     957      373288 :       sym%ses(:, :, :) = 0.0_dp
     958        4930 :       sym%sig(:, :) = 0.0_dp
     959             : 
     960         232 :       sym%center_of_mass(:) = 0.0_dp
     961             : 
     962             :       ! Initialize symmetry element counters
     963          58 :       sym%ncn = 1
     964          58 :       sym%nsn = 1
     965        1218 :       sym%nsec(:) = 0
     966        2378 :       sym%nses(:) = 0
     967          58 :       sym%nsig = 0
     968             : 
     969             :       ! Initialize logical variables
     970          58 :       sym%cubic = .FALSE.
     971          58 :       sym%dgroup = .FALSE.
     972          58 :       sym%igroup = .FALSE.
     973          58 :       sym%invers = .FALSE.
     974          58 :       sym%linear = .FALSE.
     975          58 :       sym%maxis = .FALSE.
     976          58 :       sym%ogroup = .FALSE.
     977          58 :       sym%sgroup = .FALSE.
     978          58 :       sym%sigmad = .FALSE.
     979          58 :       sym%sigmah = .FALSE.
     980          58 :       sym%sigmav = .FALSE.
     981          58 :       sym%tgroup = .FALSE.
     982             : 
     983             :       ! Initialize list of equivalent atoms (C1 symmetry)
     984          58 :       natoms = SIZE(sym%nequatom)
     985          58 :       sym%ngroup = natoms
     986         633 :       sym%nequatom(:) = 1
     987         633 :       DO i = 1, sym%ngroup
     988         575 :          sym%group_of(i) = i
     989         575 :          sym%llequatom(i) = i
     990         575 :          sym%symequ_list(i) = i
     991         633 :          sym%ulequatom(i) = i
     992             :       END DO
     993             : 
     994          58 :       sym%point_group_order = -1
     995             : 
     996         633 :       DO iatom = 1, natoms
     997         633 :          sym%aw(iatom) = weight(iatom)
     998             :       END DO
     999             : 
    1000             :       ! Generate atomic identification numbers (symmetry code) ***
    1001         633 :       sym%ain(:) = REAL(atype(:), KIND=dp) + sym%aw(:)
    1002             : 
    1003             :       ! Initialize the transformation matrix for input orientation -> standard orientation
    1004          58 :       CALL unit_matrix(sym%inptostd(:, :))
    1005             : 
    1006          58 :    END SUBROUTINE init_symmetry
    1007             : 
    1008             : ! **************************************************************************************************
    1009             : !> \brief  Return .TRUE., if the atom atoma is in the list symequ_list.
    1010             : !> \param atoma ...
    1011             : !> \param sym ...
    1012             : !> \return ...
    1013             : !> \par History
    1014             : !>        Creation (21.04.95, Matthias Krack)
    1015             : !> \author jgh
    1016             : ! **************************************************************************************************
    1017        1211 :    FUNCTION in_symequ_list(atoma, sym)
    1018             :       INTEGER, INTENT(IN)                                :: atoma
    1019             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1020             :       LOGICAL                                            :: in_symequ_list
    1021             : 
    1022             :       INTEGER                                            :: iatom
    1023             : 
    1024        1211 :       in_symequ_list = .FALSE.
    1025             : 
    1026        7969 :       DO iatom = 1, sym%ulequatom(sym%ngroup)
    1027        7969 :          IF (sym%symequ_list(iatom) == atoma) THEN
    1028        1211 :             in_symequ_list = .TRUE.
    1029             :             RETURN
    1030             :          END IF
    1031             :       END DO
    1032             : 
    1033             :    END FUNCTION in_symequ_list
    1034             : 
    1035             : ! **************************************************************************************************
    1036             : !> \brief Search for point groups of LOW SYMmetry (Ci,Cs).
    1037             : !> \param sym ...
    1038             : !> \param coord ...
    1039             : !> \par History
    1040             : !>        Creation (21.04.95, Matthias Krack)
    1041             : !> \author jgh
    1042             : ! **************************************************************************************************
    1043           3 :    SUBROUTINE lowsym(sym, coord)
    1044             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1045             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
    1046             : 
    1047             :       REAL(KIND=dp)                                      :: phi
    1048             :       REAL(KIND=dp), DIMENSION(3)                        :: d
    1049             : 
    1050           3 :       IF (sym%nsn == 2) THEN
    1051             :          ! Ci
    1052           1 :          sym%invers = .TRUE.
    1053           1 :          phi = angle(sym%ses(:, 1, 2), sym%z_axis(:))
    1054           1 :          d(:) = vector_product(sym%ses(:, 1, 2), sym%z_axis(:))
    1055           1 :          CALL rotate_molecule(phi, d(:), sym, coord)
    1056           2 :       ELSE IF (sym%nsig == 1) THEN
    1057             :          ! Cs
    1058           1 :          sym%sigmah = .TRUE.
    1059           1 :          phi = angle(sym%sig(:, 1), sym%z_axis(:))
    1060           1 :          d(:) = vector_product(sym%sig(:, 1), sym%z_axis(:))
    1061           1 :          CALL rotate_molecule(phi, d(:), sym, coord)
    1062             :       END IF
    1063             : 
    1064           3 :    END SUBROUTINE lowsym
    1065             : 
    1066             : ! **************************************************************************************************
    1067             : !> \brief Main program for symmetry analysis.
    1068             : !> \param sym ...
    1069             : !> \param eps_geo ...
    1070             : !> \param coord ...
    1071             : !> \param atype ...
    1072             : !> \param weight ...
    1073             : !> \par History
    1074             : !>        Creation (14.11.98, Matthias Krack)
    1075             : !> \author jgh
    1076             : ! **************************************************************************************************
    1077          58 :    SUBROUTINE molecular_symmetry(sym, eps_geo, coord, atype, weight)
    1078             :       TYPE(molsym_type), POINTER                         :: sym
    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
    1083             : 
    1084             :       CHARACTER(LEN=*), PARAMETER :: routineN = 'molecular_symmetry'
    1085             : 
    1086             :       INTEGER                                            :: handle, natoms
    1087             : 
    1088          58 :       CALL timeset(routineN, handle)
    1089             : 
    1090             :       ! Perform memory allocation for the symmetry analysis
    1091          58 :       natoms = SIZE(coord, 2)
    1092          58 :       CALL create_molsym(sym, natoms)
    1093          58 :       sym%eps_geo = eps_geo
    1094             : 
    1095             :       ! Initialization of symmetry analysis
    1096          58 :       CALL init_symmetry(sym, atype, weight)
    1097             : 
    1098          58 :       IF (natoms == 1) THEN
    1099             :          ! Special case: only one atom
    1100           1 :          CALL atomsym(sym)
    1101             :       ELSE
    1102             :          ! Find molecular symmetry
    1103          57 :          CALL moleculesym(sym, coord)
    1104             :          ! Get point group and load point group table
    1105          57 :          CALL get_point_group_symbol(sym)
    1106             :       END IF
    1107             : 
    1108             :       ! Calculate group order
    1109          58 :       IF (.NOT. sym%linear) CALL get_point_group_order(sym)
    1110             : 
    1111             :       ! Generate a list of equivalent atoms
    1112          58 :       CALL build_symequ_list(sym, coord)
    1113             : 
    1114          58 :       CALL timestop(handle)
    1115             : 
    1116          58 :    END SUBROUTINE molecular_symmetry
    1117             : 
    1118             : ! **************************************************************************************************
    1119             : !> \brief Find the molecular symmetry.
    1120             : !> \param sym ...
    1121             : !> \param coord ...
    1122             : !> \par History
    1123             : !>        Creation (14.11.98, Matthias Krack)
    1124             : !> \author jgh
    1125             : ! **************************************************************************************************
    1126          57 :    SUBROUTINE moleculesym(sym, coord)
    1127             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1128             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
    1129             : 
    1130             :       INTEGER                                            :: icn, isec, isn
    1131             :       LOGICAL                                            :: failed
    1132             :       REAL(KIND=dp)                                      :: eps_tenval
    1133             : 
    1134             : ! Tolerance of degenerate eigenvalues for the molecular tensor of inertia
    1135             : 
    1136          57 :       eps_tenval = 0.01_dp*sym%eps_geo
    1137             : 
    1138             :       ! Calculate the molecular tensor of inertia
    1139          57 :       CALL tensor(sym, coord)
    1140             :       ! Use symmetry information from the eigenvalues of the molecular tensor of inertia
    1141          57 :       IF ((sym%tenval(3) - sym%tenval(1)) < eps_tenval) THEN
    1142             :          ! 0 < tenval(1) = tenval(2) = tenval(3)
    1143           7 :          sym%cubic = .TRUE.
    1144          50 :       ELSE IF ((sym%tenval(3) - sym%tenval(2)) < eps_tenval) THEN
    1145             :          ! 0 < tenval(1) < tenval(2) = tenval(3)
    1146             :          ! special case: 0 = tenval(1) < tenval(2) = tenval(3)
    1147          14 :          IF (sym%tenval(1) < eps_tenval) sym%linear = .TRUE.
    1148          14 :          CALL tracar(sym, coord, (/3, 1, 2/))
    1149             :       ELSE
    1150             :          ! 0 < tenval(1) = tenval(2) < tenval(3) or 0 < tenval(1) < tenval(2) < tenval(3)
    1151          36 :          CALL tracar(sym, coord, (/1, 2, 3/))
    1152             :       END IF
    1153             : 
    1154             :       ! Continue with the new coordinate axes
    1155             :       DO
    1156          57 :          failed = .FALSE.
    1157          57 :          IF (sym%cubic) THEN
    1158           7 :             CALL cubsym(sym, coord, failed)
    1159           7 :             IF (failed) THEN
    1160           0 :                sym%cubic = .FALSE.
    1161           0 :                CYCLE
    1162             :             END IF
    1163             : 
    1164          50 :          ELSE IF (sym%linear) THEN
    1165             : 
    1166             :             ! Linear molecule
    1167           2 :             IF (saxis(2, sym%z_axis(:), sym, coord)) THEN
    1168           1 :                sym%invers = .TRUE.
    1169           1 :                sym%dgroup = .TRUE.
    1170           1 :                sym%sigmah = .TRUE.
    1171             :             END IF
    1172             : 
    1173             :          ELSE
    1174             : 
    1175             :             ! Check the new coordinate axes for Cn axes
    1176         960 :             DO icn = 2, maxcn
    1177         912 :                IF (caxis(icn, sym%z_axis(:), sym, coord)) CALL addsec(icn, sym%z_axis(:), sym)
    1178         912 :                IF (caxis(icn, sym%x_axis(:), sym, coord)) CALL addsec(icn, sym%x_axis(:), sym)
    1179         960 :                IF (caxis(icn, sym%y_axis(:), sym, coord)) CALL addsec(icn, sym%y_axis(:), sym)
    1180             :             END DO
    1181             : 
    1182             :             ! Check the new coordinate axes for Sn axes
    1183        1920 :             DO isn = 2, maxsn
    1184        1872 :                IF (saxis(isn, sym%z_axis(:), sym, coord)) CALL addses(isn, sym%z_axis(:), sym)
    1185        1872 :                IF (saxis(isn, sym%x_axis(:), sym, coord)) CALL addses(isn, sym%x_axis(:), sym)
    1186        1920 :                IF (saxis(isn, sym%y_axis(:), sym, coord)) CALL addses(isn, sym%y_axis(:), sym)
    1187             :             END DO
    1188             : 
    1189             :             ! Check the new coordinate planes for mirror planes
    1190          48 :             IF (sigma(sym%z_axis(:), sym, coord)) CALL addsig(sym%z_axis(:), sym)
    1191          48 :             IF (sigma(sym%x_axis(:), sym, coord)) CALL addsig(sym%x_axis(:), sym)
    1192          48 :             IF (sigma(sym%y_axis(:), sym, coord)) CALL addsig(sym%y_axis(:), sym)
    1193             : 
    1194             :             ! There is a main axis (MAXIS = .TRUE.)
    1195          48 :             IF ((sym%ncn > 1) .OR. (sym%nsn > 3)) THEN
    1196          45 :                sym%maxis = .TRUE.
    1197          45 :                CALL axsym(coord, sym)
    1198             :             ELSE
    1199             :                ! Only low or no symmetry (Ci, Cs or C1)
    1200           3 :                CALL lowsym(sym, coord)
    1201             :             END IF
    1202             : 
    1203             :          END IF
    1204             : 
    1205          57 :          IF (.NOT. failed) EXIT
    1206             : 
    1207             :       END DO
    1208             : 
    1209             :       ! Find the remaining S axes
    1210          57 :       IF (.NOT. sym%linear) THEN
    1211         249 :          DO icn = 2, sym%ncn
    1212         545 :             DO isec = 1, sym%nsec(icn)
    1213         296 :                IF (saxis(icn, sym%sec(:, isec, icn), sym, coord)) &
    1214          89 :                   CALL addses(icn, sym%sec(:, isec, icn), sym)
    1215         296 :                IF (saxis(2*icn, sym%sec(:, isec, icn), sym, coord)) &
    1216         243 :                   CALL addses(2*icn, sym%sec(:, isec, icn), sym)
    1217             :             END DO
    1218             :          END DO
    1219             :       END IF
    1220             : 
    1221             :       ! Remove redundant S2 axes
    1222          57 :       IF (sym%nses(2) > 0) THEN
    1223          16 :          sym%nses(1) = sym%nses(1) - sym%nses(2)
    1224          16 :          sym%nses(2) = 0
    1225          16 :          CALL addses(2, sym%z_axis(:), sym)
    1226             :       END IF
    1227             : 
    1228          57 :    END SUBROUTINE moleculesym
    1229             : 
    1230             : ! **************************************************************************************************
    1231             : !> \brief The number of atoms which are placed on an axis defined by the vector a is returned.
    1232             : !> \param a ...
    1233             : !> \param coord ...
    1234             : !> \param sym ...
    1235             : !> \return ...
    1236             : !> \par History
    1237             : !>        Creation (16.11.98, Matthias Krack)
    1238             : !> \author jgh
    1239             : ! **************************************************************************************************
    1240          76 :    FUNCTION naxis(a, coord, sym)
    1241             :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a
    1242             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
    1243             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1244             :       INTEGER                                            :: naxis
    1245             : 
    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
    1249             : 
    1250          76 :       naxis = 0
    1251          76 :       natoms = SIZE(coord, 2)
    1252             : 
    1253          76 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
    1254             : 
    1255             :       ! Check the length of vector a
    1256          76 :       IF (length_of_a > sym%eps_geo) THEN
    1257             : 
    1258         956 :          DO iatom = 1, natoms
    1259        3520 :             b(:) = coord(:, iatom)
    1260         880 :             length_of_b = SQRT(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
    1261             :             ! An atom in the origin counts for each axis
    1262         956 :             IF (length_of_b < sym%eps_geo) THEN
    1263           0 :                naxis = naxis + 1
    1264             :             ELSE
    1265        3520 :                a_norm = a(:)/length_of_a
    1266        3520 :                b_norm = b(:)/length_of_b
    1267         880 :                scapro = a_norm(1)*b_norm(1) + a_norm(2)*b_norm(2) + a_norm(3)*b_norm(3)
    1268         880 :                IF (ABS(ABS(scapro) - 1.0_dp) < sym%eps_geo) naxis = naxis + 1
    1269             :             END IF
    1270             :          END DO
    1271             : 
    1272             :       END IF
    1273             : 
    1274          76 :    END FUNCTION naxis
    1275             : 
    1276             : ! **************************************************************************************************
    1277             : !> \brief Return .FALSE., if the symmetry elements se1 and se2 are a pair of
    1278             : !>         redundant symmetry elements.
    1279             : !> \param se1 ...
    1280             : !> \param se2 ...
    1281             : !> \return ...
    1282             : !> \par History
    1283             : !>        Creation (16.11.98, Matthias Krack)
    1284             : !> \author jgh
    1285             : ! **************************************************************************************************
    1286        1802 :    FUNCTION newse(se1, se2)
    1287             :       INTEGER, INTENT(IN)                                :: se1, se2
    1288             :       LOGICAL                                            :: newse
    1289             : 
    1290             :       INTEGER                                            :: ise
    1291             : 
    1292        1802 :       newse = .TRUE.
    1293             : 
    1294        3878 :       DO ise = 2, MIN(se1, se2)
    1295        3878 :          IF ((MODULO(se1, ise) == 0) .AND. (MODULO(se2, ise) == 0)) THEN
    1296        1802 :             newse = .FALSE.
    1297             :             RETURN
    1298             :          END IF
    1299             :       END DO
    1300             : 
    1301             :    END FUNCTION newse
    1302             : 
    1303             : ! **************************************************************************************************
    1304             : !> \brief The number of atoms which are placed in a plane defined by the
    1305             : !>         the normal vector a is returned.
    1306             : !> \param a ...
    1307             : !> \param sym ...
    1308             : !> \param coord ...
    1309             : !> \return ...
    1310             : !> \par History
    1311             : !>        Creation (16.11.98, Matthias Krack)
    1312             : !> \author jgh
    1313             : ! **************************************************************************************************
    1314         105 :    FUNCTION nsigma(a, sym, coord)
    1315             :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a
    1316             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1317             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
    1318             :       INTEGER                                            :: nsigma
    1319             : 
    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
    1323             : 
    1324         105 :       nsigma = 0
    1325             : 
    1326         105 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
    1327             : 
    1328             :       ! Check the length of vector a
    1329         105 :       IF (length_of_a > sym%eps_geo) THEN
    1330         105 :          natoms = SIZE(coord, 2)
    1331         998 :          DO iatom = 1, natoms
    1332        3572 :             b(:) = coord(:, iatom)
    1333         893 :             length_of_b = SQRT(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
    1334             :             ! An atom in the origin counts for each mirror plane
    1335         998 :             IF (length_of_b < sym%eps_geo) THEN
    1336           0 :                nsigma = nsigma + 1
    1337             :             ELSE
    1338        3572 :                a_norm = a(:)/length_of_a
    1339        3572 :                b_norm = b(:)/length_of_b
    1340         893 :                scapro = a_norm(1)*b_norm(1) + a_norm(2)*b_norm(2) + a_norm(3)*b_norm(3)
    1341         893 :                IF (ABS(scapro) < sym%eps_geo) nsigma = nsigma + 1
    1342             :             END IF
    1343             :          END DO
    1344             :       END IF
    1345             : 
    1346         105 :    END FUNCTION nsigma
    1347             : 
    1348             : ! **************************************************************************************************
    1349             : !> \brief Style the output of the symmetry elements.
    1350             : !> \param se ...
    1351             : !> \param eps ...
    1352             : !> \par History
    1353             : !>        Creation (16.11.98, Matthias Krack)
    1354             : !> \author jgh
    1355             : ! **************************************************************************************************
    1356         522 :    SUBROUTINE outse(se, eps)
    1357             :       REAL(KIND=dp), DIMENSION(3), INTENT(INOUT)         :: se
    1358             :       REAL(KIND=dp), INTENT(IN)                          :: eps
    1359             : 
    1360             : ! Positive z component for all vectors
    1361             : 
    1362         522 :       IF (ABS(se(3)) < eps) THEN
    1363         255 :          IF (ABS(se(1)) < eps) THEN
    1364          47 :             se(2) = -se(2)
    1365             :          ELSE
    1366         448 :             IF (se(1) < 0.0_dp) se(1:2) = -se(1:2)
    1367             :          END IF
    1368             :       ELSE
    1369         471 :          IF (se(3) < 0.0_dp) se(:) = -se(:)
    1370             :       END IF
    1371             : 
    1372         522 :    END SUBROUTINE outse
    1373             : 
    1374             : ! **************************************************************************************************
    1375             : !> \brief Print the output of the symmetry analysis.
    1376             : !> \param sym ...
    1377             : !> \param coord ...
    1378             : !> \param atype ...
    1379             : !> \param element ...
    1380             : !> \param z ...
    1381             : !> \param weight ...
    1382             : !> \param iw ...
    1383             : !> \param plevel ...
    1384             : !> \par History
    1385             : !>        Creation (16.11.98, Matthias Krack)
    1386             : !> \author jgh
    1387             : ! **************************************************************************************************
    1388          58 :    SUBROUTINE print_symmetry(sym, coord, atype, element, z, weight, iw, plevel)
    1389             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    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
    1396             : 
    1397             :       REAL(KIND=dp), PARAMETER                           :: formatmaxval = 1.0E5_dp
    1398             : 
    1399             :       CHARACTER(LEN=3)                                   :: string
    1400             :       INTEGER                                            :: i, icn, iequatom, isec, ises, isig, isn, &
    1401             :                                                             j, nequatom, secount
    1402          58 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: equatomlist, idxlist
    1403             : 
    1404             :       ! Print point group symbol and point group order
    1405             :       WRITE (iw, "(/,(T2,A))") &
    1406          58 :          "MOLSYM| Molecular symmetry information", &
    1407         116 :          "MOLSYM|"
    1408             :       WRITE (iw, "(T2,A,T77,A4)") &
    1409          58 :          "MOLSYM| Point group", ADJUSTR(sym%point_group_symbol)
    1410          58 :       IF (sym%point_group_order > -1) THEN
    1411             :          WRITE (iw, "(T2,A,T77,I4)") &
    1412          55 :             "MOLSYM| Group order ", sym%point_group_order
    1413             :       END IF
    1414             : 
    1415          58 :       IF (MOD(plevel, 10) == 1) THEN
    1416             :          WRITE (iw, "(T2,A)") &
    1417          58 :             "MOLSYM|", &
    1418         116 :             "MOLSYM| Groups of atoms equivalent by symmetry"
    1419             :          WRITE (iw, "(T2,A,T31,A)") &
    1420          58 :             "MOLSYM| Group number", "Group members (atomic indices)"
    1421         164 :          DO i = 1, sym%ngroup
    1422         106 :             nequatom = sym%ulequatom(i) - sym%llequatom(i) + 1
    1423         106 :             CPASSERT(nequatom > 0)
    1424         424 :             ALLOCATE (equatomlist(nequatom), idxlist(nequatom))
    1425         681 :             equatomlist(1:nequatom) = sym%symequ_list(sym%llequatom(i):sym%ulequatom(i))
    1426         681 :             idxlist = 0
    1427         106 :             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         106 :                "MOLSYM|", i, nequatom, (equatomlist(iequatom), iequatom=1, nequatom)
    1430         164 :             DEALLOCATE (equatomlist, idxlist)
    1431             :          END DO
    1432             :       END IF
    1433             : 
    1434          58 :       IF (MOD(plevel/100, 10) == 1) THEN
    1435             :          ! Print all symmetry elements
    1436             :          WRITE (iw, "(T2,A,/,T2,A,/,T2,A,T31,A,T45,A,T60,A,T75,A)") &
    1437          58 :             "MOLSYM|", &
    1438          58 :             "MOLSYM| Symmetry elements", &
    1439         116 :             "MOLSYM| Element number", "Type", "X", "Y", "Z"
    1440          58 :          secount = 1
    1441             :          WRITE (iw, "(T2,A,T12,I5,T19,I5,T32,A1,T36,3(1X,F14.8))") &
    1442          58 :             "MOLSYM|", secount, secount, "E", 0.0_dp, 0.0_dp, 0.0_dp
    1443             :          ! Mirror planes
    1444          58 :          string = "@  "
    1445         211 :          DO isig = 1, sym%nsig
    1446         153 :             secount = secount + 1
    1447         153 :             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         670 :                "MOLSYM|", secount, isig, string, (sym%sig(i, isig), i=1, 3)
    1450             :          END DO
    1451             :          ! C axes
    1452         252 :          DO icn = 2, sym%ncn
    1453         194 :             IF (icn < 10) THEN
    1454         194 :                WRITE (string, "(A1,I1)") "C", icn
    1455             :             ELSE
    1456           0 :                WRITE (string, "(A1,I2)") "C", icn
    1457             :             END IF
    1458         548 :             DO isec = 1, sym%nsec(icn)
    1459         296 :                secount = secount + 1
    1460         296 :                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        1378 :                   "MOLSYM|", secount, isec, string, (sym%sec(i, isec, icn), i=1, 3)
    1463             :             END DO
    1464             :          END DO
    1465             :          ! S axes
    1466         215 :          DO isn = 2, sym%nsn
    1467         157 :             IF (isn == 2) THEN
    1468          29 :                WRITE (string, "(A3)") "i  "
    1469         128 :             ELSE IF (isn < 10) THEN
    1470         111 :                WRITE (string, "(A1,I1)") "S", isn
    1471             :             ELSE
    1472          17 :                WRITE (string, "(A1,I2)") "S", isn
    1473             :             END IF
    1474         288 :             DO ises = 1, sym%nses(isn)
    1475          73 :                secount = secount + 1
    1476          73 :                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         449 :                   "MOLSYM|", secount, ises, string, (sym%ses(i, ises, icn), i=1, 3)
    1479             :             END DO
    1480             :          END DO
    1481             :       END IF
    1482             : 
    1483          58 :       IF (MOD(plevel/10, 10) == 1 .AND. SIZE(coord, 2) > 1) THEN
    1484             :          ! Print the moments of the molecular inertia tensor
    1485             :          WRITE (iw, "(T2,A,/,T2,A,/,T2,A,T43,A,T58,A,T73,A)") &
    1486          57 :             "MOLSYM|", &
    1487          57 :             "MOLSYM| Moments of the molecular inertia tensor [a.u.]", &
    1488         114 :             "MOLSYM|", "I(x)", "I(y)", "I(z)"
    1489          57 :          string = "xyz"
    1490         741 :          IF (MAXVAL(sym%tenmat) >= formatmaxval) THEN
    1491           0 :             DO i = 1, 3
    1492             :                WRITE (iw, "(T2,A,T32,A,T36,3(1X,ES14.4))") &
    1493           0 :                   "MOLSYM|", "I("//string(i:i)//")", (sym%tenmat(i, j), j=1, 3)
    1494             :             END DO
    1495             :          ELSE
    1496         228 :             DO i = 1, 3
    1497             :                WRITE (iw, "(T2,A,T32,A,T36,3(1X,F14.8))") &
    1498         741 :                   "MOLSYM|", "I("//string(i:i)//")", (sym%tenmat(i, j), j=1, 3)
    1499             :             END DO
    1500             :          END IF
    1501             :          WRITE (iw, "(T2,A)") &
    1502          57 :             "MOLSYM|", &
    1503         114 :             "MOLSYM| Principal moments and axes of inertia [a.u.]"
    1504         741 :          IF (MAXVAL(sym%tenmat) >= formatmaxval) THEN
    1505             :             WRITE (iw, "(T2,A,T36,3(1X,ES14.4))") &
    1506           0 :                "MOLSYM|", (sym%tenval(i), i=1, 3)
    1507             :          ELSE
    1508             :             WRITE (iw, "(T2,A,T36,3(1X,F14.8))") &
    1509         228 :                "MOLSYM|", (sym%tenval(i), i=1, 3)
    1510             :          END IF
    1511         228 :          DO i = 1, 3
    1512             :             WRITE (iw, "(T2,A,T32,A,T36,3(1X,F14.8))") &
    1513         741 :                "MOLSYM|", string(i:i), (sym%tenvec(i, j), j=1, 3)
    1514             :          END DO
    1515             :       END IF
    1516             : 
    1517          58 :       IF (MOD(plevel, 10) == 1) THEN
    1518             :          ! Print the Cartesian coordinates of the standard orientation
    1519          58 :          CALL write_coordinates(coord, atype, element, z, weight, iw)
    1520             :       END IF
    1521             : 
    1522          58 :    END SUBROUTINE print_symmetry
    1523             : 
    1524             : ! **************************************************************************************************
    1525             : !> \brief Rotate the molecule about an axis defined by the vector a. The
    1526             : !>        rotation angle is phi (radians).
    1527             : !> \param phi ...
    1528             : !> \param a ...
    1529             : !> \param sym ...
    1530             : !> \param coord ...
    1531             : !> \par History
    1532             : !>        Creation (16.11.98, Matthias Krack)
    1533             : !> \author jgh
    1534             : ! **************************************************************************************************
    1535          87 :    SUBROUTINE rotate_molecule(phi, a, sym, coord)
    1536             :       REAL(KIND=dp), INTENT(IN)                          :: phi
    1537             :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a
    1538             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1539             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
    1540             : 
    1541             :       INTEGER                                            :: i
    1542             :       REAL(KIND=dp)                                      :: length_of_a
    1543             : 
    1544             :       ! Check the length of vector a
    1545          87 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
    1546          87 :       IF (length_of_a > sym%eps_geo) THEN
    1547             : 
    1548             :          ! Build up the rotation matrix
    1549          42 :          CALL build_rotmat(phi, a(:), sym%rotmat(:, :))
    1550             : 
    1551             :          ! Rotate the molecule by phi around a
    1552       10689 :          coord(:, :) = MATMUL(sym%rotmat(:, :), coord(:, :))
    1553             : 
    1554             :          ! Rotate the normal vectors of the mirror planes which are already found
    1555        3339 :          sym%sig(:, 1:sym%nsig) = MATMUL(sym%rotmat(:, :), sym%sig(:, 1:sym%nsig))
    1556             : 
    1557             :          ! Rotate the Cn axes which are already found
    1558         186 :          DO i = 2, sym%ncn
    1559        9696 :             sym%sec(:, 1:sym%nsec(i), i) = MATMUL(sym%rotmat(:, :), sym%sec(:, 1:sym%nsec(i), i))
    1560             :          END DO
    1561             : 
    1562             :          ! Rotate the Sn axes which are already found
    1563         134 :          DO i = 2, sym%nsn
    1564        3014 :             sym%ses(:, 1:sym%nses(i), i) = MATMUL(sym%rotmat(:, :), sym%ses(:, 1:sym%nses(i), i))
    1565             :          END DO
    1566             : 
    1567             :          ! Store current rotation
    1568        2688 :          sym%inptostd(:, :) = MATMUL(sym%rotmat(:, :), sym%inptostd(:, :))
    1569             : 
    1570             :       END IF
    1571             : 
    1572          87 :    END SUBROUTINE rotate_molecule
    1573             : 
    1574             : ! **************************************************************************************************
    1575             : !> \brief Rotate the molecule about a n-fold axis defined by the vector a
    1576             : !>        using a rotation angle of 2*pi/n. Check, if the axis is a Cn axis.
    1577             : !> \param n ...
    1578             : !> \param a ...
    1579             : !> \param sym ...
    1580             : !> \param coord ...
    1581             : !> \return ...
    1582             : !> \par History
    1583             : !>        Creation (16.11.98, Matthias Krack)
    1584             : !> \author jgh
    1585             : ! **************************************************************************************************
    1586        6344 :    FUNCTION saxis(n, a, sym, coord)
    1587             :       INTEGER, INTENT(IN)                                :: n
    1588             :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a
    1589             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1590             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
    1591             :       LOGICAL                                            :: saxis
    1592             : 
    1593             :       INTEGER                                            :: iatom, natoms
    1594             :       REAL(KIND=dp)                                      :: length_of_a, phi
    1595             :       REAL(KIND=dp), DIMENSION(3)                        :: b
    1596             : 
    1597        6344 :       saxis = .FALSE.
    1598             : 
    1599        6344 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
    1600             : 
    1601        6344 :       natoms = SIZE(coord, 2)
    1602             : 
    1603             :       ! Check the length of the axis vector a
    1604        6344 :       IF (length_of_a > sym%eps_geo) THEN
    1605             :          ! Calculate the rotation angle phi and build up the rotation matrix rotmat
    1606        6344 :          phi = 2.0_dp*pi/REAL(n, KIND=dp)
    1607        6344 :          CALL build_rotmat(phi, a(:), sym%rotmat(:, :))
    1608             :          ! Check all atoms
    1609        9816 :          DO iatom = 1, natoms
    1610             :             ! Rotate the actual atom by 2*pi/n about a
    1611      124111 :             b(:) = MATMUL(sym%rotmat(:, :), coord(:, iatom))
    1612             :             ! Reflect the coordinates of the rotated atom
    1613       38188 :             b(:) = reflect_vector(b(:), a(:))
    1614             :             ! Check if the coordinates of the rotated atom are in the coordinate set of the molecule
    1615        9816 :             IF (equatom(iatom, b(:), sym, coord) == 0) RETURN
    1616             :          END DO
    1617             :          ! The axis defined by a is a Sn axis, thus return with saxis = .TRUE.
    1618             :          saxis = .TRUE.
    1619             :       END IF
    1620             : 
    1621             :    END FUNCTION saxis
    1622             : 
    1623             : ! **************************************************************************************************
    1624             : !> \brief Reflect all atoms of the molecule through a mirror plane defined
    1625             : !>        by the normal vector a.
    1626             : !> \param a ...
    1627             : !> \param sym ...
    1628             : !> \param coord ...
    1629             : !> \return ...
    1630             : !> \par History
    1631             : !>        Creation (16.11.98, Matthias Krack)
    1632             : !> \author jgh
    1633             : ! **************************************************************************************************
    1634        2808 :    FUNCTION sigma(a, sym, coord)
    1635             :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a
    1636             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1637             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
    1638             :       LOGICAL                                            :: sigma
    1639             : 
    1640             :       INTEGER                                            :: iatom, natoms
    1641             :       REAL(KIND=dp)                                      :: length_of_a
    1642             :       REAL(KIND=dp), DIMENSION(3)                        :: b
    1643             : 
    1644        2808 :       sigma = .FALSE.
    1645             : 
    1646        2808 :       length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
    1647             : 
    1648             :       ! Check the length of vector a
    1649        2808 :       IF (length_of_a > sym%eps_geo) THEN
    1650        2674 :          natoms = SIZE(coord, 2)
    1651       10068 :          DO iatom = 1, natoms
    1652             :             ! Reflect the actual atom
    1653        9471 :             b(:) = reflect_vector(coord(:, iatom), a(:))
    1654             :             ! Check if the coordinates of the reflected atom are in the coordinate set of the molecule
    1655       10068 :             IF (equatom(iatom, b(:), sym, coord) == 0) RETURN
    1656             :          END DO
    1657             :          ! The plane defined by a is a mirror plane, thus return with sigma = .TRUE.
    1658             :          sigma = .TRUE.
    1659             :       END IF
    1660             : 
    1661             :    END FUNCTION sigma
    1662             : 
    1663             : ! **************************************************************************************************
    1664             : !> \brief Calculate the molecular tensor of inertia and the vector to the
    1665             : !>        center of mass of the molecule.
    1666             : !> \param sym ...
    1667             : !> \param coord ...
    1668             : !> \par History
    1669             : !>        Creation (16.11.98, Matthias Krack)
    1670             : !> \author jgh
    1671             : ! **************************************************************************************************
    1672          57 :    SUBROUTINE tensor(sym, coord)
    1673             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1674             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
    1675             : 
    1676             :       INTEGER                                            :: natoms
    1677             :       REAL(KIND=dp)                                      :: tt
    1678             : 
    1679             :       ! Find the vector center_of_mass to molecular center of mass
    1680          57 :       natoms = SIZE(coord, 2)
    1681        3269 :       sym%center_of_mass(:) = MATMUL(coord(:, 1:natoms), sym%aw(1:natoms))/SUM(sym%aw(1:natoms))
    1682             : 
    1683             :       ! Translate the center of mass of the molecule to the origin
    1684        2353 :       coord(:, 1:natoms) = coord(:, 1:natoms) - SPREAD(sym%center_of_mass(:), 2, natoms)
    1685             : 
    1686             :       ! Build up the molecular tensor of inertia
    1687             : 
    1688         631 :       sym%tenmat(1, 1) = DOT_PRODUCT(sym%aw(1:natoms), (coord(2, 1:natoms)**2 + coord(3, 1:natoms)**2))
    1689         631 :       sym%tenmat(2, 2) = DOT_PRODUCT(sym%aw(1:natoms), (coord(3, 1:natoms)**2 + coord(1, 1:natoms)**2))
    1690         631 :       sym%tenmat(3, 3) = DOT_PRODUCT(sym%aw(1:natoms), (coord(1, 1:natoms)**2 + coord(2, 1:natoms)**2))
    1691             : 
    1692         631 :       sym%tenmat(1, 2) = -DOT_PRODUCT(sym%aw(1:natoms), (coord(1, 1:natoms)*coord(2, 1:natoms)))
    1693         631 :       sym%tenmat(1, 3) = -DOT_PRODUCT(sym%aw(1:natoms), (coord(1, 1:natoms)*coord(3, 1:natoms)))
    1694         631 :       sym%tenmat(2, 3) = -DOT_PRODUCT(sym%aw(1:natoms), (coord(2, 1:natoms)*coord(3, 1:natoms)))
    1695             : 
    1696             :       ! Symmetrize tensor matrix
    1697          57 :       sym%tenmat(2, 1) = sym%tenmat(1, 2)
    1698          57 :       sym%tenmat(3, 1) = sym%tenmat(1, 3)
    1699          57 :       sym%tenmat(3, 2) = sym%tenmat(2, 3)
    1700             : 
    1701             :       ! Diagonalize the molecular tensor of inertia
    1702          57 :       CALL jacobi(sym%tenmat(:, :), sym%tenval(:), sym%tenvec(:, :))
    1703             : 
    1704             :       ! Secure that the principal axes are right-handed
    1705         228 :       sym%tenvec(:, 3) = vector_product(sym%tenvec(:, 1), sym%tenvec(:, 2))
    1706             : 
    1707          57 :       tt = SQRT(sym%tenval(1)**2 + sym%tenval(2)**2 + sym%tenval(3)**2)
    1708          57 :       CPASSERT(tt /= 0)
    1709             : 
    1710          57 :    END SUBROUTINE tensor
    1711             : 
    1712             : ! **************************************************************************************************
    1713             : !> \brief Transformation the Cartesian coodinates with the matrix tenvec.
    1714             : !>        The coordinate axes can be switched according to the index
    1715             : !>        vector idx. If idx(1) is equal to 3 instead to 1, then the first
    1716             : !>        eigenvector (smallest eigenvalue) of the molecular tensor of
    1717             : !>        inertia is connected to the z axis instead of the x axis. In
    1718             : !>        addition the local atomic coordinate systems are initialized,
    1719             : !>        if the symmetry is turned off.
    1720             : !> \param sym ...
    1721             : !> \param coord ...
    1722             : !> \param idx ...
    1723             : !> \par History
    1724             : !>        Creation (16.11.98, Matthias Krack)
    1725             : !> \author jgh
    1726             : ! **************************************************************************************************
    1727          50 :    SUBROUTINE tracar(sym, coord, idx)
    1728             :       TYPE(molsym_type), INTENT(inout)                   :: sym
    1729             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(inout)      :: coord
    1730             :       INTEGER, DIMENSION(3), INTENT(IN)                  :: idx
    1731             : 
    1732             :       INTEGER                                            :: iatom, natom
    1733             :       REAL(KIND=dp), DIMENSION(3, 3)                     :: tenvec
    1734             : 
    1735             :       ! Transformation of the atomic coordinates  ***
    1736          50 :       natom = SIZE(coord, 2)
    1737         650 :       tenvec = TRANSPOSE(sym%tenvec)
    1738         482 :       DO iatom = 1, natom
    1739        3074 :          coord(idx, iatom) = MATMUL(tenvec, coord(:, iatom))
    1740             :       END DO
    1741             : 
    1742             :       ! Modify the transformation matrix for input orientation -> standard orientation
    1743         650 :       sym%inptostd(idx, :) = tenvec
    1744             : 
    1745          50 :    END SUBROUTINE tracar
    1746             : 
    1747             : ! **************************************************************************************************
    1748             : !> \brief Distance between two points
    1749             : !> \param a ...
    1750             : !> \param b ...
    1751             : !> \return ...
    1752             : !> \author jgh
    1753             : ! **************************************************************************************************
    1754        2864 :    FUNCTION dist(a, b) RESULT(d)
    1755             :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: a, b
    1756             :       REAL(KIND=dp)                                      :: d
    1757             : 
    1758       11456 :       d = SQRT(SUM((a - b)**2))
    1759             : 
    1760        2864 :    END FUNCTION
    1761             : ! **************************************************************************************************
    1762             : !> \brief   Write atomic coordinates to output unit iw.
    1763             : !> \param coord ...
    1764             : !> \param atype ...
    1765             : !> \param element ...
    1766             : !> \param z ...
    1767             : !> \param weight ...
    1768             : !> \param iw ...
    1769             : !> \date    08.05.2008
    1770             : !> \author  JGH
    1771             : ! **************************************************************************************************
    1772          58 :    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
    1779             : 
    1780             :       INTEGER                                            :: iatom, natom
    1781             : 
    1782          58 :       IF (iw > 0) THEN
    1783          58 :          natom = SIZE(coord, 2)
    1784             :          WRITE (UNIT=iw, FMT="(T2,A)") &
    1785          58 :             "MOLSYM|", &
    1786         116 :             "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          58 :             "MOLSYM|   Atom Kind Element", "X", "Y", "Z", "Mass"
    1789         633 :          DO iatom = 1, natom
    1790             :             WRITE (UNIT=iw, FMT="(T2,A,I7,1X,I4,1X,A2,1X,I4,3(1X,F13.8),1X,F9.4)") &
    1791         575 :                "MOLSYM|", iatom, atype(iatom), ADJUSTL(element(iatom)), z(iatom), &
    1792        1208 :                coord(1:3, iatom), weight(iatom)
    1793             :          END DO
    1794             :          WRITE (UNIT=iw, FMT="(T2,A)") &
    1795          58 :             "MOLSYM|", &
    1796         116 :             "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          58 :             "MOLSYM|   Atom Kind Element", "X", "Y", "Z", "Mass"
    1799         633 :          DO iatom = 1, natom
    1800             :             WRITE (UNIT=iw, FMT="(T2,A,I7,1X,I4,1X,A2,1X,I4,3(1X,F13.8),1X,F9.4)") &
    1801         575 :                "MOLSYM|", iatom, atype(iatom), ADJUSTL(element(iatom)), z(iatom), &
    1802        2933 :                coord(1:3, iatom)*angstrom, weight(iatom)
    1803             :          END DO
    1804             :       END IF
    1805             : 
    1806          58 :    END SUBROUTINE write_coordinates
    1807             : 
    1808           0 : END MODULE molsym

Generated by: LCOV version 1.15