!--------------------------------------------------------------------------------------------------!
!   CP2K: A general program to perform molecular dynamics simulations                              !
!   Copyright 2000-2025 CP2K developers group <https://cp2k.org>                                   !
!                                                                                                  !
!   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
!--------------------------------------------------------------------------------------------------!

! **************************************************************************************************
MODULE topology_gromos
   USE cp_log_handling,                 ONLY: cp_get_default_logger,&
                                              cp_logger_type
   USE cp_output_handling,              ONLY: cp_print_key_finished_output,&
                                              cp_print_key_unit_nr
   USE cp_parser_methods,               ONLY: parser_get_next_line,&
                                              parser_get_object,&
                                              parser_search_string
   USE cp_parser_types,                 ONLY: cp_parser_type,&
                                              parser_create,&
                                              parser_release
   USE cp_units,                        ONLY: cp_unit_to_cp2k
   USE input_cp2k_restarts_util,        ONLY: section_velocity_val_set
   USE input_section_types,             ONLY: section_get_rval,&
                                              section_vals_get_subs_vals,&
                                              section_vals_type
   USE kinds,                           ONLY: default_string_length,&
                                              dp
   USE memory_utilities,                ONLY: reallocate
   USE message_passing,                 ONLY: mp_para_env_type
   USE string_table,                    ONLY: s2s,&
                                              str2id
   USE topology_types,                  ONLY: atom_info_type,&
                                              connectivity_info_type,&
                                              topology_parameters_type
#include "./base/base_uses.f90"

   IMPLICIT NONE

   CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'topology_gromos'

   PRIVATE
   PUBLIC :: read_topology_gromos, read_coordinate_g96

CONTAINS

! **************************************************************************************************
!> \brief Read GROMOS topology file
!> \param file_name ...
!> \param topology ...
!> \param para_env ...
!> \param subsys_section ...
! **************************************************************************************************
   SUBROUTINE read_topology_gromos(file_name, topology, para_env, subsys_section)
      CHARACTER(LEN=*), INTENT(IN)                       :: file_name
      TYPE(topology_parameters_type), INTENT(INOUT)      :: topology
      TYPE(mp_para_env_type), POINTER                    :: para_env
      TYPE(section_vals_type), POINTER                   :: subsys_section

      CHARACTER(len=*), PARAMETER :: routineN = 'read_topology_gromos'

      CHARACTER                                          :: ctemp
      CHARACTER(LEN=default_string_length)               :: label, string
      CHARACTER(LEN=default_string_length), &
         DIMENSION(21)                                   :: avail_section
      CHARACTER(LEN=default_string_length), POINTER      :: namearray1(:), namearray2(:)
      INTEGER :: begin, handle, i, iatom, ibond, icon, ii(50), index_now, iresid, isolvent, itemp, &
         itype, iw, jatom, natom, natom_prev, nbond, nbond_prev, ncon, nsolute, nsolvent, ntype, &
         offset, offset2, stat
      INTEGER, POINTER                                   :: ba(:), bb(:), na(:)
      LOGICAL                                            :: found
      REAL(KIND=dp)                                      :: ftemp
      REAL(KIND=dp), POINTER                             :: ac(:), am(:)
      TYPE(atom_info_type), POINTER                      :: atom_info
      TYPE(connectivity_info_type), POINTER              :: conn_info
      TYPE(cp_logger_type), POINTER                      :: logger
      TYPE(cp_parser_type)                               :: parser

      NULLIFY (logger)
      NULLIFY (namearray1, namearray2)
      logger => cp_get_default_logger()
      iw = cp_print_key_unit_nr(logger, subsys_section, "PRINT%TOPOLOGY_INFO/GTOP_INFO", &
                                extension=".subsysLog")
      CALL timeset(routineN, handle)

      avail_section(1) = "TITLE"
      avail_section(2) = "TOPPHYSCON"
      avail_section(3) = "TOPVERSION"
      avail_section(4) = "ATOMTYPENAME"
      avail_section(5) = "RESNAME"
      avail_section(6) = "SOLUTEATOM"
      avail_section(7) = "BONDTYPE"
      avail_section(8) = "BONDH"
      avail_section(9) = "BOND"
      avail_section(10) = "BONDANGLETYPE"
      avail_section(11) = "BONDANGLEH"
      avail_section(12) = "BONDANGLE"
      avail_section(13) = "IMPDIHEDRALTYPE"
      avail_section(14) = "IMPDIHEDRALH"
      avail_section(15) = "IMPDIHEDRAL"
      avail_section(16) = "DIHEDRALTYPE"
      avail_section(17) = "DIHEDRALH"
      avail_section(18) = "DIHEDRAL"
      avail_section(19) = "LJPARAMETERS"
      avail_section(20) = "SOLVENTATOM"
      avail_section(21) = "SOLVENTCONSTR"

      atom_info => topology%atom_info
      conn_info => topology%conn_info

      natom_prev = 0
      IF (ASSOCIATED(atom_info%id_molname)) natom_prev = SIZE(atom_info%id_molname)
      ! TITLE SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the TITLE section'
      CALL parser_create(parser, file_name, para_env=para_env)
      label = TRIM(avail_section(1))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         DO
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, string, string_length=80)
            IF (string == TRIM("END")) EXIT
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| ", TRIM(string)
         END DO
      END IF
      CALL parser_release(parser)
      ! TOPPHYSCON SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the TOPPHYSCON section'
      CALL parser_create(parser, file_name, para_env=para_env)
      label = TRIM(avail_section(2))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         DO
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, string)
            IF (string == TRIM("END")) EXIT
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO|  ", TRIM(string)
         END DO
      END IF
      CALL parser_release(parser)
      ! TOPVERSION SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the TOPVERSION section'
      CALL parser_create(parser, file_name, para_env=para_env)
      label = TRIM(avail_section(3))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         DO
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, string)
            IF (string == TRIM("END")) EXIT
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO|  ", TRIM(string)
         END DO
      END IF
      CALL parser_release(parser)
      ! ATOMTYPENAME SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the ATOMTYPENAME section'
      CALL parser_create(parser, file_name, para_env=para_env)
      label = TRIM(avail_section(4))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         CALL reallocate(namearray1, 1, ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, namearray1(itype))
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO|  ", TRIM(namearray1(itype))
         END DO
      END IF
      CALL parser_release(parser)
      ! RESNAME SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the RESNAME section'
      CALL parser_create(parser, file_name, para_env=para_env)
      label = TRIM(avail_section(5))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         CALL reallocate(namearray2, 1, ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, namearray2(itype))
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO|  ", TRIM(namearray2(itype))
         END DO
      END IF
      CALL parser_release(parser)
      ! SOLUTEATOM SECTION
      iresid = 1
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the SOLUTEATOM section'
      CALL parser_create(parser, file_name, para_env=para_env)
      label = TRIM(avail_section(6))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, natom)
         CALL reallocate(atom_info%id_molname, 1, natom_prev + natom)
         CALL reallocate(atom_info%resid, 1, natom_prev + natom)
         CALL reallocate(atom_info%id_resname, 1, natom_prev + natom)
         CALL reallocate(atom_info%id_atmname, 1, natom_prev + natom)
         CALL reallocate(atom_info%id_element, 1, natom_prev + natom)
         CALL reallocate(atom_info%atm_charge, 1, natom_prev + natom)
         CALL reallocate(atom_info%atm_mass, 1, natom_prev + natom)
         CALL parser_get_next_line(parser, 1)
         DO iatom = 1, natom
            index_now = iatom + natom_prev
            CALL parser_get_object(parser, itemp)
            CALL parser_get_object(parser, itemp)
            atom_info%resid(index_now) = itemp
            atom_info%id_molname(index_now) = str2id(s2s(namearray2(itemp)))
            atom_info%id_resname(index_now) = str2id(s2s(namearray2(itemp)))
            CALL parser_get_object(parser, string)
            CALL parser_get_object(parser, itemp)
            atom_info%id_atmname(index_now) = str2id(s2s(namearray1(itemp)))
            atom_info%id_element(index_now) = str2id(s2s(namearray1(itemp)))
            CALL parser_get_object(parser, atom_info%atm_mass(index_now))
            CALL parser_get_object(parser, atom_info%atm_charge(index_now))
            CALL parser_get_object(parser, itemp)
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT SOLUTEATOM INFO HERE!"
            CALL parser_get_object(parser, ntype)
            DO i = 1, 50
               ii(i) = -1
            END DO
            stat = -1
            begin = 1
            IF (ntype /= 0) THEN
               DO
                  IF (begin .EQ. 1) THEN
                     READ (parser%input_line, iostat=stat, FMT=*) itemp, itemp, ctemp, itemp, ftemp, ftemp, &
                        itemp, itemp, (ii(i), i=begin, ntype)
                  ELSE IF (begin .GT. 1) THEN
                     CALL parser_get_next_line(parser, 1)
                     READ (parser%input_line, iostat=stat, FMT=*) (ii(i), i=begin, ntype)
                  END IF
                  DO i = ntype, 1, -1
                     IF (ii(i) .LT. 0) THEN
                        begin = i
                     END IF
                  END DO
                  IF (stat .EQ. 0) EXIT
               END DO
            END IF
            ! 1-4 list
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, ntype)
            IF (ntype /= 0) THEN
               itemp = (itemp - 1)/6 + 1
               offset = 0
               IF (ASSOCIATED(conn_info%onfo_a)) offset = SIZE(conn_info%onfo_a)
               CALL reallocate(conn_info%onfo_a, 1, offset + ntype)
               CALL reallocate(conn_info%onfo_b, 1, offset + ntype)
               conn_info%onfo_a(offset + 1:offset + ntype) = index_now
               DO i = 1, 50
                  ii(i) = -1
               END DO
               stat = -1
               begin = 1
               IF (ntype /= 0) THEN
                  DO
                     IF (begin .EQ. 1) THEN
                        READ (parser%input_line, iostat=stat, FMT=*) itemp, (ii(i), i=begin, ntype)
                     ELSE IF (begin .GT. 1) THEN
                        CALL parser_get_next_line(parser, 1)
                        READ (parser%input_line, iostat=stat, FMT=*) (ii(i), i=begin, ntype)
                     END IF
                     DO i = ntype, 1, -1
                        IF (ii(i) .LT. 0) THEN
                           begin = i
                        END IF
                     END DO
                     IF (stat .EQ. 0) EXIT
                  END DO
                  DO i = 1, ntype
                     conn_info%onfo_b(offset + i) = ii(i)
                  END DO
               END IF
               CALL parser_get_next_line(parser, 1)
            ELSE
               CALL parser_get_next_line(parser, 1)
            END IF
         END DO
      END IF
      nsolute = natom
      CALL parser_release(parser)

      CALL parser_create(parser, file_name, para_env=para_env)
      ! BONDH SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the BONDH section'
      label = TRIM(avail_section(8))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         offset = 0
         IF (ASSOCIATED(conn_info%bond_a)) offset = SIZE(conn_info%bond_a)
         CALL reallocate(conn_info%bond_a, 1, offset + ntype)
         CALL reallocate(conn_info%bond_b, 1, offset + ntype)
         CALL reallocate(conn_info%bond_type, 1, offset + ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, conn_info%bond_a(offset + itype))
            CALL parser_get_object(parser, conn_info%bond_b(offset + itype))
            CALL parser_get_object(parser, itemp)
            conn_info%bond_type(offset + itype) = itemp
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT BONDH INFO HERE!"
         END DO
         conn_info%bond_a(offset + 1:offset + ntype) = conn_info%bond_a(offset + 1:offset + ntype) + natom_prev
         conn_info%bond_b(offset + 1:offset + ntype) = conn_info%bond_b(offset + 1:offset + ntype) + natom_prev
      END IF
      ! BOND SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the BOND section'
      label = TRIM(avail_section(9))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         offset = 0
         IF (ASSOCIATED(conn_info%bond_a)) offset = SIZE(conn_info%bond_a)
         CALL reallocate(conn_info%bond_a, 1, offset + ntype)
         CALL reallocate(conn_info%bond_b, 1, offset + ntype)
         CALL reallocate(conn_info%bond_type, 1, offset + ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, conn_info%bond_a(offset + itype))
            CALL parser_get_object(parser, conn_info%bond_b(offset + itype))
            CALL parser_get_object(parser, itemp)
            conn_info%bond_type(offset + itype) = itemp
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT BOND INFO HERE!"
         END DO
         conn_info%bond_a(offset + 1:offset + ntype) = conn_info%bond_a(offset + 1:offset + ntype) + natom_prev
         conn_info%bond_b(offset + 1:offset + ntype) = conn_info%bond_b(offset + 1:offset + ntype) + natom_prev
      END IF
      ! BONDANGLEH SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the BONDANGLEH section'
      label = TRIM(avail_section(11))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         offset = 0
         IF (ASSOCIATED(conn_info%theta_a)) offset = SIZE(conn_info%theta_a)
         CALL reallocate(conn_info%theta_a, 1, offset + ntype)
         CALL reallocate(conn_info%theta_b, 1, offset + ntype)
         CALL reallocate(conn_info%theta_c, 1, offset + ntype)
         CALL reallocate(conn_info%theta_type, 1, offset + ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, conn_info%theta_a(offset + itype))
            CALL parser_get_object(parser, conn_info%theta_b(offset + itype))
            CALL parser_get_object(parser, conn_info%theta_c(offset + itype))
            CALL parser_get_object(parser, itemp)
            conn_info%theta_type(offset + itype) = itemp
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT BONDANGLEH INFO HERE!"
         END DO
         conn_info%theta_a(offset + 1:offset + ntype) = conn_info%theta_a(offset + 1:offset + ntype) + natom_prev
         conn_info%theta_b(offset + 1:offset + ntype) = conn_info%theta_b(offset + 1:offset + ntype) + natom_prev
         conn_info%theta_c(offset + 1:offset + ntype) = conn_info%theta_c(offset + 1:offset + ntype) + natom_prev
      END IF
      ! BONDANGLE SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the BONDANGLE section'
      label = TRIM(avail_section(12))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         offset = 0
         IF (ASSOCIATED(conn_info%theta_a)) offset = SIZE(conn_info%theta_a)
         CALL reallocate(conn_info%theta_a, 1, offset + ntype)
         CALL reallocate(conn_info%theta_b, 1, offset + ntype)
         CALL reallocate(conn_info%theta_c, 1, offset + ntype)
         CALL reallocate(conn_info%theta_type, 1, offset + ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, conn_info%theta_a(offset + itype))
            CALL parser_get_object(parser, conn_info%theta_b(offset + itype))
            CALL parser_get_object(parser, conn_info%theta_c(offset + itype))
            CALL parser_get_object(parser, itemp)
            conn_info%theta_type(offset + itype) = itemp
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT BONDANGLE INFO HERE!"
         END DO
         conn_info%theta_a(offset + 1:offset + ntype) = conn_info%theta_a(offset + 1:offset + ntype) + natom_prev
         conn_info%theta_b(offset + 1:offset + ntype) = conn_info%theta_b(offset + 1:offset + ntype) + natom_prev
         conn_info%theta_c(offset + 1:offset + ntype) = conn_info%theta_c(offset + 1:offset + ntype) + natom_prev
      END IF
      ! IMPDIHEDRALH SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the IMPDIHEDRALH section'
      label = TRIM(avail_section(14))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         offset = 0
         IF (ASSOCIATED(conn_info%impr_a)) offset = SIZE(conn_info%impr_a)
         CALL reallocate(conn_info%impr_a, 1, offset + ntype)
         CALL reallocate(conn_info%impr_b, 1, offset + ntype)
         CALL reallocate(conn_info%impr_c, 1, offset + ntype)
         CALL reallocate(conn_info%impr_d, 1, offset + ntype)
         CALL reallocate(conn_info%impr_type, 1, offset + ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, conn_info%impr_a(offset + itype))
            CALL parser_get_object(parser, conn_info%impr_b(offset + itype))
            CALL parser_get_object(parser, conn_info%impr_c(offset + itype))
            CALL parser_get_object(parser, conn_info%impr_d(offset + itype))
            CALL parser_get_object(parser, itemp)
            conn_info%impr_type(offset + itype) = itemp
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT IMPDIHEDRALH INFO HERE!"
         END DO
         conn_info%impr_a(offset + 1:offset + ntype) = conn_info%impr_a(offset + 1:offset + ntype) + natom_prev
         conn_info%impr_b(offset + 1:offset + ntype) = conn_info%impr_b(offset + 1:offset + ntype) + natom_prev
         conn_info%impr_c(offset + 1:offset + ntype) = conn_info%impr_c(offset + 1:offset + ntype) + natom_prev
         conn_info%impr_d(offset + 1:offset + ntype) = conn_info%impr_d(offset + 1:offset + ntype) + natom_prev
      END IF
      ! IMPDIHEDRAL SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the IMPDIHEDRAL section'
      label = TRIM(avail_section(15))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         offset = 0
         IF (ASSOCIATED(conn_info%impr_a)) offset = SIZE(conn_info%impr_a)
         CALL reallocate(conn_info%impr_a, 1, offset + ntype)
         CALL reallocate(conn_info%impr_b, 1, offset + ntype)
         CALL reallocate(conn_info%impr_c, 1, offset + ntype)
         CALL reallocate(conn_info%impr_d, 1, offset + ntype)
         CALL reallocate(conn_info%impr_type, 1, offset + ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, conn_info%impr_a(offset + itype))
            CALL parser_get_object(parser, conn_info%impr_b(offset + itype))
            CALL parser_get_object(parser, conn_info%impr_c(offset + itype))
            CALL parser_get_object(parser, conn_info%impr_d(offset + itype))
            CALL parser_get_object(parser, itemp)
            conn_info%impr_type(offset + itype) = itemp
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT IMPDIHEDRAL INFO HERE!"
         END DO
         conn_info%impr_a(offset + 1:offset + ntype) = conn_info%impr_a(offset + 1:offset + ntype) + natom_prev
         conn_info%impr_b(offset + 1:offset + ntype) = conn_info%impr_b(offset + 1:offset + ntype) + natom_prev
         conn_info%impr_c(offset + 1:offset + ntype) = conn_info%impr_c(offset + 1:offset + ntype) + natom_prev
         conn_info%impr_d(offset + 1:offset + ntype) = conn_info%impr_d(offset + 1:offset + ntype) + natom_prev
      END IF
      ! DIHEDRALH SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the DIHEDRALH section'
      label = TRIM(avail_section(17))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         offset = 0
         IF (ASSOCIATED(conn_info%phi_a)) offset = SIZE(conn_info%phi_a)
         CALL reallocate(conn_info%phi_a, 1, offset + ntype)
         CALL reallocate(conn_info%phi_b, 1, offset + ntype)
         CALL reallocate(conn_info%phi_c, 1, offset + ntype)
         CALL reallocate(conn_info%phi_d, 1, offset + ntype)
         CALL reallocate(conn_info%phi_type, 1, offset + ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, conn_info%phi_a(offset + itype))
            CALL parser_get_object(parser, conn_info%phi_b(offset + itype))
            CALL parser_get_object(parser, conn_info%phi_c(offset + itype))
            CALL parser_get_object(parser, conn_info%phi_d(offset + itype))
            CALL parser_get_object(parser, itemp)
            conn_info%phi_type(offset + itype) = itemp
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT DIHEDRALH INFO HERE!"
         END DO
         conn_info%phi_a(offset + 1:offset + ntype) = conn_info%phi_a(offset + 1:offset + ntype) + natom_prev
         conn_info%phi_b(offset + 1:offset + ntype) = conn_info%phi_b(offset + 1:offset + ntype) + natom_prev
         conn_info%phi_c(offset + 1:offset + ntype) = conn_info%phi_c(offset + 1:offset + ntype) + natom_prev
         conn_info%phi_d(offset + 1:offset + ntype) = conn_info%phi_d(offset + 1:offset + ntype) + natom_prev
      END IF
      ! DIHEDRAL SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the DIHEDRAL section'
      label = TRIM(avail_section(18))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ntype)
         offset = 0
         IF (ASSOCIATED(conn_info%phi_a)) offset = SIZE(conn_info%phi_a)
         CALL reallocate(conn_info%phi_a, 1, offset + ntype)
         CALL reallocate(conn_info%phi_b, 1, offset + ntype)
         CALL reallocate(conn_info%phi_c, 1, offset + ntype)
         CALL reallocate(conn_info%phi_d, 1, offset + ntype)
         CALL reallocate(conn_info%phi_type, 1, offset + ntype)
         DO itype = 1, ntype
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, conn_info%phi_a(offset + itype))
            CALL parser_get_object(parser, conn_info%phi_b(offset + itype))
            CALL parser_get_object(parser, conn_info%phi_c(offset + itype))
            CALL parser_get_object(parser, conn_info%phi_d(offset + itype))
            CALL parser_get_object(parser, itemp)
            conn_info%phi_type(offset + itype) = itemp
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT DIHEDRAL INFO HERE!"
         END DO
         conn_info%phi_a(offset + 1:offset + ntype) = conn_info%phi_a(offset + 1:offset + ntype) + natom_prev
         conn_info%phi_b(offset + 1:offset + ntype) = conn_info%phi_b(offset + 1:offset + ntype) + natom_prev
         conn_info%phi_c(offset + 1:offset + ntype) = conn_info%phi_c(offset + 1:offset + ntype) + natom_prev
         conn_info%phi_d(offset + 1:offset + ntype) = conn_info%phi_d(offset + 1:offset + ntype) + natom_prev
      END IF
      CALL parser_release(parser)

      ! SOLVENTATOM and SOLVENTCONSTR SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'GTOP_INFO| Parsing the SOLVENTATOM section'
      nsolvent = (SIZE(atom_info%r(1, :)) - nsolute)/3

      NULLIFY (na, am, ac, ba, bb)
      CALL parser_create(parser, file_name, para_env=para_env)
      label = TRIM(avail_section(20))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, natom)
         CALL reallocate(na, 1, natom)
         CALL reallocate(am, 1, natom)
         CALL reallocate(ac, 1, natom)
         DO iatom = 1, natom
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, itemp)
            CALL parser_get_object(parser, string)
            CALL parser_get_object(parser, na(iatom))
            CALL parser_get_object(parser, am(iatom))
            CALL parser_get_object(parser, ac(iatom))
            IF (iw > 0) WRITE (iw, *) "GTOP_INFO| PUT SOLVENTATOM INFO HERE!"
         END DO
      END IF
      label = TRIM(avail_section(21))
      ncon = 0
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, ncon)
         CALL reallocate(ba, 1, ncon)
         CALL reallocate(bb, 1, ncon)
         DO icon = 1, ncon
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, ba(icon))
            CALL parser_get_object(parser, bb(icon))
         END DO
      END IF
      CALL parser_release(parser)

      offset = 0
      IF (ASSOCIATED(atom_info%id_molname)) offset = SIZE(atom_info%id_molname)
      CALL reallocate(atom_info%id_molname, 1, offset + nsolvent*natom)
      CALL reallocate(atom_info%resid, 1, offset + nsolvent*natom)
      CALL reallocate(atom_info%id_resname, 1, offset + nsolvent*natom)
      CALL reallocate(atom_info%id_atmname, 1, offset + nsolvent*natom)
      CALL reallocate(atom_info%id_element, 1, offset + nsolvent*natom)
      CALL reallocate(atom_info%atm_charge, 1, offset + nsolvent*natom)
      CALL reallocate(atom_info%atm_mass, 1, offset + nsolvent*natom)
      DO isolvent = 1, nsolvent
         offset = nsolute + natom*isolvent - natom
         DO iatom = 1, natom
            index_now = offset + iatom
            atom_info%id_atmname(index_now) = str2id(s2s(namearray1(na(iatom))))
            atom_info%id_element(index_now) = str2id(s2s(namearray1(na(iatom))))
            atom_info%id_molname(index_now) = str2id(s2s("SOL"))
            atom_info%id_resname(index_now) = str2id(s2s("SOL"))
            atom_info%resid(index_now) = isolvent
            atom_info%atm_mass(index_now) = am(iatom)
            atom_info%atm_charge(index_now) = ac(iatom)
         END DO
      END DO

      offset = 0
      IF (ASSOCIATED(conn_info%bond_a)) offset = SIZE(conn_info%bond_a)
      offset2 = MAXVAL(conn_info%bond_type(:))
      CALL reallocate(conn_info%bond_a, 1, offset + ncon*nsolvent)
      CALL reallocate(conn_info%bond_b, 1, offset + ncon*nsolvent)
      CALL reallocate(conn_info%bond_type, 1, offset + ncon*nsolvent)
      offset = offset - ncon
      DO isolvent = 1, nsolvent
         offset = offset + ncon
         DO icon = 1, ncon
            conn_info%bond_a(offset + icon) = nsolute + isolvent*ncon - ncon + ba(icon)
            conn_info%bond_b(offset + icon) = nsolute + isolvent*ncon - ncon + bb(icon)
            conn_info%bond_type(offset + icon) = offset2 + isolvent*ncon - ncon + icon
         END DO
      END DO
      ! PARA_RES structure
      i = 0
      nbond_prev = 0
      IF (ASSOCIATED(conn_info%c_bond_a)) i = SIZE(conn_info%c_bond_a)
      nbond = SIZE(conn_info%bond_a)
      DO ibond = 1 + nbond_prev, nbond + nbond_prev
         iatom = conn_info%bond_a(ibond)
         jatom = conn_info%bond_b(ibond)
         IF (topology%para_res) THEN
            IF ((atom_info%id_molname(iatom) /= atom_info%id_molname(jatom)) .OR. &
                (atom_info%resid(iatom) /= atom_info%resid(jatom)) .OR. &
                (atom_info%id_resname(iatom) /= atom_info%id_resname(jatom))) THEN
               IF (iw > 0) WRITE (iw, '(T2,A,2I3)') "GTOP_INFO| PARA_RES, bond between molecules atom ", &
                  iatom, jatom
               i = i + 1
               CALL reallocate(conn_info%c_bond_a, 1, i)
               CALL reallocate(conn_info%c_bond_b, 1, i)
               CALL reallocate(conn_info%c_bond_type, 1, i)
               conn_info%c_bond_a(i) = iatom
               conn_info%c_bond_b(i) = jatom
               conn_info%c_bond_type(i) = conn_info%bond_type(ibond)
            END IF
         ELSE
            IF (atom_info%id_molname(iatom) /= atom_info%id_molname(jatom)) THEN
               CPABORT("")
            END IF
         END IF
      END DO

      DEALLOCATE (namearray1)
      DEALLOCATE (namearray2)
      DEALLOCATE (na)
      DEALLOCATE (am)
      DEALLOCATE (ac)
      IF (ASSOCIATED(ba)) &
         DEALLOCATE (ba)
      IF (ASSOCIATED(bb)) &
         DEALLOCATE (bb)

      CALL timestop(handle)
      CALL cp_print_key_finished_output(iw, logger, subsys_section, &
                                        "PRINT%TOPOLOGY_INFO/GTOP_INFO")

   END SUBROUTINE read_topology_gromos

! **************************************************************************************************
!> \brief ...
!> \param topology ...
!> \param para_env ...
!> \param subsys_section ...
! **************************************************************************************************
   SUBROUTINE read_coordinate_g96(topology, para_env, subsys_section)
      TYPE(topology_parameters_type)                     :: topology
      TYPE(mp_para_env_type), POINTER                    :: para_env
      TYPE(section_vals_type), POINTER                   :: subsys_section

      CHARACTER(len=*), PARAMETER :: routineN = 'read_coordinate_g96'
      INTEGER, PARAMETER                                 :: nblock = 1000

      CHARACTER(LEN=default_string_length)               :: label, string, strtmp, strtmp2
      CHARACTER(LEN=default_string_length), DIMENSION(5) :: avail_section
      INTEGER                                            :: handle, itemp, iw, natom, newsize
      LOGICAL                                            :: found
      REAL(KIND=dp)                                      :: pfactor
      REAL(KIND=dp), DIMENSION(:, :), POINTER            :: velocity
      TYPE(atom_info_type), POINTER                      :: atom_info
      TYPE(cp_logger_type), POINTER                      :: logger
      TYPE(cp_parser_type)                               :: parser
      TYPE(section_vals_type), POINTER                   :: velocity_section

      NULLIFY (logger, velocity)
      logger => cp_get_default_logger()
      iw = cp_print_key_unit_nr(logger, subsys_section, "PRINT%TOPOLOGY_INFO/G96_INFO", &
                                extension=".subsysLog")
      CALL timeset(routineN, handle)

      pfactor = section_get_rval(subsys_section, "TOPOLOGY%MEMORY_PROGRESSION_FACTOR")
      atom_info => topology%atom_info
      IF (iw > 0) WRITE (iw, *) "    Reading in G96 file ", TRIM(topology%coord_file_name)
      avail_section(1) = "TITLE"
      avail_section(2) = "TIMESTEP"
      avail_section(3) = "POSITION"
      avail_section(4) = "VELOCITY"
      avail_section(5) = "BOX"

      ! Element is assigned on the basis of the atm_name
      topology%aa_element = .TRUE.

      CALL reallocate(atom_info%id_molname, 1, nblock)
      CALL reallocate(atom_info%id_resname, 1, nblock)
      CALL reallocate(atom_info%resid, 1, nblock)
      CALL reallocate(atom_info%id_atmname, 1, nblock)
      CALL reallocate(atom_info%id_element, 1, nblock)
      CALL reallocate(atom_info%r, 1, 3, 1, nblock)
      CALL reallocate(atom_info%atm_mass, 1, nblock)
      CALL reallocate(atom_info%atm_charge, 1, nblock)
      CALL reallocate(atom_info%occup, 1, nblock)
      CALL reallocate(atom_info%beta, 1, nblock)
      ! TITLE SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'G96_INFO| Parsing the TITLE section'
      CALL parser_create(parser, topology%coord_file_name, para_env=para_env)
      label = TRIM(avail_section(1))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         DO
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, string, string_length=default_string_length)
            IF (string == TRIM("END")) EXIT
            IF (iw > 0) WRITE (iw, *) "G96_INFO|   ", TRIM(string)
         END DO
      END IF
      CALL parser_release(parser)
      ! POSITION SECTION
      IF (iw > 0) WRITE (iw, '(T2,A)') 'G96_INFO| Parsing the POSITION section'
      CALL parser_create(parser, topology%coord_file_name, para_env=para_env)
      label = TRIM(avail_section(3))
      CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
      IF (found) THEN
         natom = 0
         CALL parser_get_next_line(parser, 1)
         CALL parser_get_object(parser, string, string_length=default_string_length)
         DO
            IF (string == TRIM("END")) EXIT
            natom = natom + 1
            IF (natom > SIZE(atom_info%id_molname)) THEN
               newsize = INT(pfactor*natom)
               CALL reallocate(atom_info%id_molname, 1, newsize)
               CALL reallocate(atom_info%id_resname, 1, newsize)
               CALL reallocate(atom_info%resid, 1, newsize)
               CALL reallocate(atom_info%id_atmname, 1, newsize)
               CALL reallocate(atom_info%id_element, 1, newsize)
               CALL reallocate(atom_info%r, 1, 3, 1, newsize)
               CALL reallocate(atom_info%atm_mass, 1, newsize)
               CALL reallocate(atom_info%atm_charge, 1, newsize)
               CALL reallocate(atom_info%occup, 1, newsize)
               CALL reallocate(atom_info%beta, 1, newsize)
            END IF
            READ (string, *) &
               atom_info%resid(natom), strtmp, strtmp2, &
               itemp, atom_info%r(1, natom), atom_info%r(2, natom), atom_info%r(3, natom)
            atom_info%id_resname(natom) = str2id(s2s(strtmp))
            atom_info%id_atmname(natom) = str2id(s2s(strtmp2))
            atom_info%id_molname(natom) = atom_info%id_resname(natom)
            atom_info%id_element(natom) = atom_info%id_atmname(natom)
            atom_info%r(1, natom) = cp_unit_to_cp2k(atom_info%r(1, natom), "nm")
            atom_info%r(2, natom) = cp_unit_to_cp2k(atom_info%r(2, natom), "nm")
            atom_info%r(3, natom) = cp_unit_to_cp2k(atom_info%r(3, natom), "nm")
            IF (iw > 0) WRITE (iw, *) "G96_INFO| PUT POSITION INFO HERE!"
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, string, string_length=default_string_length)
         END DO
      END IF
      CALL parser_release(parser)
      CALL reallocate(velocity, 1, 3, 1, natom)
      ! VELOCITY SECTION
      IF (topology%use_g96_velocity) THEN
         IF (iw > 0) WRITE (iw, '(T2,A)') 'G96_INFO| Parsing the VELOCITY section'
         CALL parser_create(parser, topology%coord_file_name, para_env=para_env)
         label = TRIM(avail_section(4))
         CALL parser_search_string(parser, label, .TRUE., found, begin_line=.TRUE.)
         IF (found) THEN
            natom = 0
            CALL parser_get_next_line(parser, 1)
            CALL parser_get_object(parser, string, string_length=default_string_length)
            DO
               IF (string == TRIM("END")) EXIT
               natom = natom + 1
               READ (string, *) &
                  atom_info%resid(natom), strtmp, strtmp2, &
                  itemp, velocity(1, natom), velocity(2, natom), velocity(3, natom)
               atom_info%id_resname(natom) = str2id(strtmp)
               atom_info%id_atmname(natom) = str2id(strtmp2)
               atom_info%id_molname(natom) = atom_info%id_resname(natom)
               atom_info%id_element(natom) = atom_info%id_atmname(natom)
               velocity(1, natom) = cp_unit_to_cp2k(velocity(1, natom), "nm*ps^-1")
               velocity(2, natom) = cp_unit_to_cp2k(velocity(2, natom), "nm*ps^-1")
               velocity(3, natom) = cp_unit_to_cp2k(velocity(3, natom), "nm*ps^-1")
               IF (iw > 0) WRITE (iw, *) "G96_INFO| PUT VELOCITY INFO HERE!"
               CALL parser_get_next_line(parser, 1)
               CALL parser_get_object(parser, string, string_length=default_string_length)
            END DO
            CALL parser_release(parser)
            velocity_section => section_vals_get_subs_vals(subsys_section, "VELOCITY")
            CALL section_velocity_val_set(velocity_section, velocity=velocity, &
                                          conv_factor=1.0_dp)
         END IF
      END IF
      DEALLOCATE (velocity)

      CALL reallocate(atom_info%id_molname, 1, natom)
      CALL reallocate(atom_info%id_resname, 1, natom)
      CALL reallocate(atom_info%resid, 1, natom)
      CALL reallocate(atom_info%id_atmname, 1, natom)
      CALL reallocate(atom_info%id_element, 1, natom)
      CALL reallocate(atom_info%r, 1, 3, 1, natom)
      CALL reallocate(atom_info%atm_mass, 1, natom)
      CALL reallocate(atom_info%atm_charge, 1, natom)
      CALL reallocate(atom_info%occup, 1, natom)
      CALL reallocate(atom_info%beta, 1, natom)

      IF (.NOT. topology%para_res) atom_info%resid(:) = 1

      topology%natoms = natom
      CALL cp_print_key_finished_output(iw, logger, subsys_section, &
                                        "PRINT%TOPOLOGY_INFO/G96_INFO")
      CALL timestop(handle)

   END SUBROUTINE read_coordinate_g96

END MODULE topology_gromos

