LCOV - code coverage report
Current view: top level - src - particle_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 92.5 % 599 554
Test Date: 2026-09-03 07:32:15 Functions: 100.0 % 8 8

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief Define methods related to particle_type
      10              : !> \par History
      11              : !>            10.2014 Move routines out of particle_types.F [Ole Schuett]
      12              : !> \author Ole Schuett
      13              : ! **************************************************************************************************
      14              : MODULE particle_methods
      15              :    USE atomic_kind_types,               ONLY: get_atomic_kind
      16              :    USE basis_set_types,                 ONLY: get_gto_basis_set,&
      17              :                                               gto_basis_set_p_type
      18              :    USE cell_methods,                    ONLY: cell_create,&
      19              :                                               set_cell_param
      20              :    USE cell_types,                      ONLY: cell_clone,&
      21              :                                               cell_release,&
      22              :                                               cell_type,&
      23              :                                               get_cell,&
      24              :                                               pbc,&
      25              :                                               real_to_scaled
      26              :    USE cp2k_info,                       ONLY: compile_revision,&
      27              :                                               cp2k_version,&
      28              :                                               r_cwd,&
      29              :                                               r_host_name,&
      30              :                                               r_user_name
      31              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      32              :                                               cp_logger_get_default_io_unit,&
      33              :                                               cp_logger_type,&
      34              :                                               cp_to_string
      35              :    USE cp_output_handling,              ONLY: cp_p_file,&
      36              :                                               cp_print_key_finished_output,&
      37              :                                               cp_print_key_generate_filename,&
      38              :                                               cp_print_key_should_output,&
      39              :                                               cp_print_key_unit_nr
      40              :    USE cp_units,                        ONLY: cp_unit_from_cp2k
      41              :    USE external_potential_types,        ONLY: fist_potential_type,&
      42              :                                               get_potential
      43              :    USE input_constants,                 ONLY: dump_atomic,&
      44              :                                               dump_dcd,&
      45              :                                               dump_dcd_aligned_cell,&
      46              :                                               dump_extxyz,&
      47              :                                               dump_pdb,&
      48              :                                               dump_xmol
      49              :    USE input_cp2k_subsys,               ONLY: create_cell_section
      50              :    USE input_enumeration_types,         ONLY: enum_i2c,&
      51              :                                               enumeration_type
      52              :    USE input_keyword_types,             ONLY: keyword_get,&
      53              :                                               keyword_type
      54              :    USE input_section_types,             ONLY: section_get_keyword,&
      55              :                                               section_release,&
      56              :                                               section_type,&
      57              :                                               section_vals_get_subs_vals,&
      58              :                                               section_vals_type,&
      59              :                                               section_vals_val_get
      60              :    USE kinds,                           ONLY: default_path_length,&
      61              :                                               default_string_length,&
      62              :                                               dp,&
      63              :                                               sp
      64              :    USE machine,                         ONLY: m_timestamp,&
      65              :                                               timestamp_length
      66              :    USE mathconstants,                   ONLY: degree
      67              :    USE mathlib,                         ONLY: angle,&
      68              :                                               dihedral_angle,&
      69              :                                               gcd
      70              :    USE memory_utilities,                ONLY: reallocate
      71              :    USE particle_types,                  ONLY: get_particle_pos_or_vel,&
      72              :                                               particle_type
      73              :    USE periodic_table,                  ONLY: nelem
      74              :    USE physcon,                         ONLY: massunit
      75              :    USE qmmm_ff_fist,                    ONLY: qmmm_ff_precond_only_qm
      76              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      77              :                                               qs_kind_type
      78              :    USE shell_potential_types,           ONLY: get_shell,&
      79              :                                               shell_kind_type
      80              :    USE string_utilities,                ONLY: uppercase
      81              :    USE util,                            ONLY: sort,&
      82              :                                               sort_unique
      83              : #include "./base/base_uses.f90"
      84              : 
      85              :    IMPLICIT NONE
      86              : 
      87              :    PRIVATE
      88              : 
      89              :    ! Public subroutines
      90              : 
      91              :    PUBLIC :: write_fist_particle_coordinates, &
      92              :              write_qs_particle_coordinates, &
      93              :              write_particle_distances, &
      94              :              write_particle_coordinates, &
      95              :              write_structure_data, &
      96              :              get_particle_set, &
      97              :              write_particle_matrix, &
      98              :              write_final_structure
      99              : 
     100              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'particle_methods'
     101              : 
     102              : CONTAINS
     103              : 
     104              : ! **************************************************************************************************
     105              : !> \brief   Get the components of a particle set.
     106              : !> \param particle_set ...
     107              : !> \param qs_kind_set ...
     108              : !> \param first_sgf ...
     109              : !> \param last_sgf ...
     110              : !> \param nsgf ...
     111              : !> \param nmao ...
     112              : !> \param basis ...
     113              : !> \param ncgf ...
     114              : !> \date    14.01.2002
     115              : !> \par History
     116              : !>      - particle type cleaned (13.10.2003,MK)
     117              : !>      - refactoring and add basis set option (17.08.2010,jhu)
     118              : !> \author  MK
     119              : !> \version 1.0
     120              : ! **************************************************************************************************
     121       210466 :    SUBROUTINE get_particle_set(particle_set, qs_kind_set, first_sgf, last_sgf, nsgf, &
     122       210466 :                                nmao, basis, ncgf)
     123              : 
     124              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     125              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     126              :       INTEGER, DIMENSION(:), INTENT(INOUT), OPTIONAL     :: first_sgf, last_sgf, nsgf, nmao
     127              :       TYPE(gto_basis_set_p_type), DIMENSION(:), OPTIONAL :: basis
     128              :       INTEGER, DIMENSION(:), INTENT(INOUT), OPTIONAL     :: ncgf
     129              : 
     130              :       INTEGER                                            :: ikind, iparticle, isgf, nparticle, ns
     131              : 
     132       210466 :       CPASSERT(ASSOCIATED(particle_set))
     133              : 
     134       210466 :       nparticle = SIZE(particle_set)
     135       210466 :       IF (PRESENT(first_sgf)) THEN
     136        49176 :          CPASSERT(SIZE(first_sgf) >= nparticle)
     137              :       END IF
     138       210466 :       IF (PRESENT(last_sgf)) THEN
     139        39282 :          CPASSERT(SIZE(last_sgf) >= nparticle)
     140              :       END IF
     141       210466 :       IF (PRESENT(nsgf)) THEN
     142       160840 :          CPASSERT(SIZE(nsgf) >= nparticle)
     143              :       END IF
     144       210466 :       IF (PRESENT(nmao)) THEN
     145           14 :          CPASSERT(SIZE(nmao) >= nparticle)
     146              :       END IF
     147       210466 :       IF (PRESENT(ncgf)) THEN
     148            4 :          CPASSERT(SIZE(ncgf) >= nparticle)
     149              :       END IF
     150              : 
     151       210466 :       IF (PRESENT(first_sgf) .OR. PRESENT(last_sgf) .OR. PRESENT(nsgf)) THEN
     152              :          isgf = 0
     153      1164007 :          DO iparticle = 1, nparticle
     154       954039 :             CALL get_atomic_kind(particle_set(iparticle)%atomic_kind, kind_number=ikind)
     155       954039 :             IF (PRESENT(basis)) THEN
     156       714913 :                IF (ASSOCIATED(basis(ikind)%gto_basis_set)) THEN
     157       714909 :                   CALL get_gto_basis_set(gto_basis_set=basis(ikind)%gto_basis_set, nsgf=ns)
     158              :                ELSE
     159            4 :                   ns = 0
     160              :                END IF
     161              :             ELSE
     162       239126 :                CALL get_qs_kind(qs_kind_set(ikind), nsgf=ns)
     163              :             END IF
     164       954039 :             IF (PRESENT(nsgf)) nsgf(iparticle) = ns
     165       954039 :             IF (PRESENT(first_sgf)) first_sgf(iparticle) = isgf + 1
     166       954039 :             isgf = isgf + ns
     167      2118544 :             IF (PRESENT(last_sgf)) last_sgf(iparticle) = isgf
     168              :          END DO
     169              :       END IF
     170              : 
     171       210466 :       IF (PRESENT(ncgf)) THEN
     172           12 :          DO iparticle = 1, nparticle
     173            8 :             CALL get_atomic_kind(particle_set(iparticle)%atomic_kind, kind_number=ikind)
     174            8 :             IF (PRESENT(basis)) THEN
     175            8 :                IF (ASSOCIATED(basis(ikind)%gto_basis_set)) THEN
     176            8 :                   CALL get_gto_basis_set(gto_basis_set=basis(ikind)%gto_basis_set, ncgf=ns)
     177              :                ELSE
     178            0 :                   ns = 0
     179              :                END IF
     180              :             ELSE
     181            0 :                CALL get_qs_kind(qs_kind_set(ikind), ncgf=ns)
     182              :             END IF
     183           20 :             ncgf(iparticle) = ns
     184              :          END DO
     185              :       END IF
     186              : 
     187       210466 :       IF (PRESENT(first_sgf)) THEN
     188        49176 :          IF (SIZE(first_sgf) > nparticle) first_sgf(nparticle + 1) = isgf + 1
     189              :       END IF
     190              : 
     191       210466 :       IF (PRESENT(nmao)) THEN
     192           86 :          DO iparticle = 1, nparticle
     193           72 :             CALL get_atomic_kind(particle_set(iparticle)%atomic_kind, kind_number=ikind)
     194           72 :             CALL get_qs_kind(qs_kind_set(ikind), mao=ns)
     195           86 :             nmao(iparticle) = ns
     196              :          END DO
     197              :       END IF
     198              : 
     199       210466 :    END SUBROUTINE get_particle_set
     200              : 
     201              : ! **************************************************************************************************
     202              : !> \brief   Should be able to write a few formats e.g. xmol, and some binary
     203              : !>          format (dcd) some format can be used for x,v,f
     204              : !>
     205              : !>          FORMAT   CONTENT                                    UNITS x, v, f
     206              : !>          XMOL     POS, VEL, FORCE, POS_VEL, POS_VEL_FORCE    Angstrom, a.u., a.u.
     207              : !>
     208              : !> \param particle_set ...
     209              : !> \param iunit ...
     210              : !> \param output_format ...
     211              : !> \param content ...
     212              : !> \param title ...
     213              : !> \param cell ...
     214              : !> \param array ...
     215              : !> \param unit_conv ...
     216              : !> \param charge_occup ...
     217              : !> \param charge_beta ...
     218              : !> \param charge_extended ...
     219              : !> \param print_kind ...
     220              : !> \date    14.01.2002
     221              : !> \author  MK
     222              : !> \version 1.0
     223              : ! **************************************************************************************************
     224        27258 :    SUBROUTINE write_particle_coordinates(particle_set, iunit, output_format, &
     225        27258 :                                          content, title, cell, array, unit_conv, &
     226              :                                          charge_occup, charge_beta, &
     227              :                                          charge_extended, print_kind)
     228              : 
     229              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     230              :       INTEGER                                            :: iunit, output_format
     231              :       CHARACTER(LEN=*)                                   :: content, title
     232              :       TYPE(cell_type), OPTIONAL, POINTER                 :: cell
     233              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL  :: array
     234              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: unit_conv
     235              :       LOGICAL, INTENT(IN), OPTIONAL                      :: charge_occup, charge_beta, &
     236              :                                                             charge_extended, print_kind
     237              : 
     238              :       CHARACTER(len=*), PARAMETER :: routineN = 'write_particle_coordinates'
     239              : 
     240              :       CHARACTER(LEN=120)                                 :: line
     241              :       CHARACTER(LEN=2)                                   :: element_symbol
     242              :       CHARACTER(LEN=4)                                   :: name
     243              :       CHARACTER(LEN=default_string_length)               :: atm_name, my_format
     244              :       INTEGER                                            :: handle, iatom, natom
     245              :       LOGICAL                                            :: dummy, my_charge_beta, &
     246              :                                                             my_charge_extended, my_charge_occup, &
     247              :                                                             my_print_kind
     248              :       REAL(KIND=dp)                                      :: angle_alpha, angle_beta, angle_gamma, &
     249              :                                                             factor, qeff
     250        27258 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: arr
     251              :       REAL(KIND=dp), DIMENSION(3)                        :: abc, angles, f, r, v
     252              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: h
     253        27258 :       REAL(KIND=sp), ALLOCATABLE, DIMENSION(:)           :: x4, y4, z4
     254              :       TYPE(cell_type), POINTER                           :: cell_dcd
     255              :       TYPE(fist_potential_type), POINTER                 :: fist_potential
     256              :       TYPE(shell_kind_type), POINTER                     :: shell
     257              : 
     258        27258 :       CALL timeset(routineN, handle)
     259              : 
     260        27258 :       natom = SIZE(particle_set)
     261        27258 :       IF (PRESENT(array)) THEN
     262         1848 :          SELECT CASE (TRIM(content))
     263              :          CASE ("POS_VEL", "POS_VEL_FORCE")
     264         1848 :             CPABORT("Illegal usage")
     265              :          END SELECT
     266              :       END IF
     267        27258 :       factor = 1.0_dp
     268        27258 :       IF (PRESENT(unit_conv)) THEN
     269        27107 :          factor = unit_conv
     270              :       END IF
     271        54443 :       SELECT CASE (output_format)
     272              :       CASE (dump_xmol, dump_extxyz)
     273        27185 :          my_print_kind = .FALSE.
     274        27185 :          IF (PRESENT(print_kind)) my_print_kind = print_kind
     275        27185 :          WRITE (iunit, "(I8)") natom
     276        27185 :          WRITE (iunit, "(A)") TRIM(title)
     277      1349570 :          DO iatom = 1, natom
     278              :             CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
     279      1322385 :                                  element_symbol=element_symbol)
     280      1322385 :             IF (LEN_TRIM(element_symbol) == 0 .OR. my_print_kind) THEN
     281              :                CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
     282           24 :                                     name=atm_name)
     283           24 :                dummy = qmmm_ff_precond_only_qm(id1=atm_name)
     284           24 :                my_format = "(A,"
     285           24 :                atm_name = TRIM(atm_name)
     286              :             ELSE
     287      1322361 :                my_format = "(T2,A2,"
     288      1322361 :                atm_name = TRIM(element_symbol)
     289              :             END IF
     290        27185 :             SELECT CASE (TRIM(content))
     291              :             CASE ("POS")
     292      1207049 :                IF (PRESENT(array)) THEN
     293        53912 :                   r(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
     294              :                ELSE
     295      4612548 :                   r(:) = particle_set(iatom)%r(:)
     296              :                END IF
     297      4828196 :                WRITE (iunit, TRIM(my_format)//"1X,3F20.10)") TRIM(atm_name), r(1:3)*factor
     298              :             CASE ("VEL")
     299        85772 :                IF (PRESENT(array)) THEN
     300            0 :                   v(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
     301              :                ELSE
     302       343088 :                   v(:) = particle_set(iatom)%v(:)
     303              :                END IF
     304       343088 :                WRITE (iunit, TRIM(my_format)//"1X,3F20.10)") TRIM(atm_name), v(1:3)*factor
     305              :             CASE ("FORCE")
     306        20955 :                IF (PRESENT(array)) THEN
     307            0 :                   f(:) = array((iatom - 1)*3 + 1:(iatom - 1)*3 + 3)
     308              :                ELSE
     309        83820 :                   f(:) = particle_set(iatom)%f(:)
     310              :                END IF
     311        83820 :                WRITE (iunit, TRIM(my_format)//"1X,3F20.10)") TRIM(atm_name), f(1:3)*factor
     312              :             CASE ("FORCE_MIXING_LABELS")
     313         8609 :                IF (PRESENT(array)) THEN
     314        34436 :                   f(:) = array((iatom - 1)*3 + 1:(iatom - 1)*3 + 3)
     315              :                ELSE
     316            0 :                   f(:) = particle_set(iatom)%f(:)
     317              :                END IF
     318      1356821 :                WRITE (iunit, TRIM(my_format)//"1X,3F20.10)") TRIM(atm_name), f(1:3)*factor
     319              :             END SELECT
     320              :          END DO
     321              :       CASE (dump_atomic)
     322          170 :          DO iatom = 1, natom
     323           10 :             SELECT CASE (TRIM(content))
     324              :             CASE ("POS")
     325          160 :                IF (PRESENT(array)) THEN
     326            0 :                   r(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
     327              :                ELSE
     328          640 :                   r(:) = particle_set(iatom)%r(:)
     329              :                END IF
     330          640 :                WRITE (iunit, "(3F20.10)") r(1:3)*factor
     331              :             CASE ("VEL")
     332            0 :                IF (PRESENT(array)) THEN
     333            0 :                   v(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
     334              :                ELSE
     335            0 :                   v(:) = particle_set(iatom)%v(:)
     336              :                END IF
     337            0 :                WRITE (iunit, "(3F20.10)") v(1:3)*factor
     338              :             CASE ("FORCE")
     339            0 :                IF (PRESENT(array)) THEN
     340            0 :                   f(:) = array((iatom - 1)*3 + 1:(iatom - 1)*3 + 3)
     341              :                ELSE
     342            0 :                   f(:) = particle_set(iatom)%f(:)
     343              :                END IF
     344            0 :                WRITE (iunit, "(3F20.10)") f(1:3)*factor
     345              :             CASE ("FORCE_MIXING_LABELS")
     346            0 :                IF (PRESENT(array)) THEN
     347            0 :                   f(:) = array((iatom - 1)*3 + 1:(iatom - 1)*3 + 3)
     348              :                ELSE
     349            0 :                   f(:) = particle_set(iatom)%f(:)
     350              :                END IF
     351          160 :                WRITE (iunit, "(3F20.10)") f(1:3)*factor
     352              :             END SELECT
     353              :          END DO
     354              :       CASE (dump_dcd, dump_dcd_aligned_cell)
     355            4 :          IF (.NOT. (PRESENT(cell))) THEN
     356            0 :             CPABORT("Cell is not present! Report this bug!")
     357              :          END IF
     358              :          CALL get_cell(cell, alpha=angle_alpha, beta=angle_beta, gamma=angle_gamma, &
     359            4 :                        abc=abc)
     360            4 :          IF (.NOT. cell%orthorhombic .AND. (output_format == dump_dcd_aligned_cell)) THEN
     361              :             ! In the case of a non-orthorhombic cell adopt a common convention
     362              :             ! for the orientation of the cell with respect to the Cartesian axes:
     363              :             ! Cell vector a is aligned with the x axis and the cell vector b lies
     364              :             ! in the xy plane.
     365            0 :             NULLIFY (cell_dcd)
     366            0 :             CALL cell_create(cell_dcd)
     367            0 :             CALL cell_clone(cell, cell_dcd, tag="CELL_DCD")
     368            0 :             angles(1) = angle_alpha/degree
     369            0 :             angles(2) = angle_beta/degree
     370            0 :             angles(3) = angle_gamma/degree
     371              :             CALL set_cell_param(cell_dcd, abc, angles, &
     372            0 :                                 do_init_cell=.TRUE.)
     373            0 :             h(1:3, 1:3) = MATMUL(cell_dcd%hmat(1:3, 1:3), cell%h_inv(1:3, 1:3))
     374            0 :             CALL cell_release(cell_dcd)
     375              :          END IF
     376           12 :          ALLOCATE (arr(3, natom))
     377            4 :          IF (PRESENT(array)) THEN
     378            0 :             arr(1:3, 1:natom) = RESHAPE(array, [3, natom])
     379              :          ELSE
     380            8 :             SELECT CASE (TRIM(content))
     381              :             CASE ("POS")
     382         1156 :                DO iatom = 1, natom
     383         4612 :                   arr(1:3, iatom) = particle_set(iatom)%r(1:3)
     384              :                END DO
     385              :             CASE ("VEL")
     386            0 :                DO iatom = 1, natom
     387            0 :                   arr(1:3, iatom) = particle_set(iatom)%v(1:3)
     388              :                END DO
     389              :             CASE ("FORCE")
     390            0 :                DO iatom = 1, natom
     391            0 :                   arr(1:3, iatom) = particle_set(iatom)%f(1:3)
     392              :                END DO
     393              :             CASE DEFAULT
     394            4 :                CPABORT("Illegal DCD dump type")
     395              :             END SELECT
     396              :          END IF
     397           12 :          ALLOCATE (x4(natom))
     398            8 :          ALLOCATE (y4(natom))
     399            8 :          ALLOCATE (z4(natom))
     400            4 :          IF (.NOT. cell%orthorhombic .AND. (output_format == dump_dcd_aligned_cell)) THEN
     401            0 :             x4(1:natom) = REAL(MATMUL(h(1, 1:3), arr(1:3, 1:natom)), KIND=sp)
     402            0 :             y4(1:natom) = REAL(MATMUL(h(2, 1:3), arr(1:3, 1:natom)), KIND=sp)
     403            0 :             z4(1:natom) = REAL(MATMUL(h(3, 1:3), arr(1:3, 1:natom)), KIND=sp)
     404              :          ELSE
     405         1156 :             x4(1:natom) = REAL(arr(1, 1:natom), KIND=sp)
     406         1156 :             y4(1:natom) = REAL(arr(2, 1:natom), KIND=sp)
     407         1156 :             z4(1:natom) = REAL(arr(3, 1:natom), KIND=sp)
     408              :          END IF
     409            4 :          WRITE (iunit) abc(1)*factor, angle_gamma, abc(2)*factor, &
     410            8 :             angle_beta, angle_alpha, abc(3)*factor
     411         1156 :          WRITE (iunit) x4*REAL(factor, KIND=sp)
     412         1156 :          WRITE (iunit) y4*REAL(factor, KIND=sp)
     413         1156 :          WRITE (iunit) z4*REAL(factor, KIND=sp)
     414              :          ! Release work storage
     415            4 :          DEALLOCATE (arr)
     416            4 :          DEALLOCATE (x4)
     417            4 :          DEALLOCATE (y4)
     418            8 :          DEALLOCATE (z4)
     419              :       CASE (dump_pdb)
     420           59 :          my_charge_occup = .FALSE.
     421           59 :          IF (PRESENT(charge_occup)) my_charge_occup = charge_occup
     422           59 :          my_charge_beta = .FALSE.
     423           59 :          IF (PRESENT(charge_beta)) my_charge_beta = charge_beta
     424           59 :          my_charge_extended = .FALSE.
     425           59 :          IF (PRESENT(charge_extended)) my_charge_extended = charge_extended
     426           59 :          IF (LEN_TRIM(title) > 0) THEN
     427              :             WRITE (UNIT=iunit, FMT="(A6,T11,A)") &
     428           59 :                "REMARK", TRIM(title)
     429              :          END IF
     430           59 :          CALL get_cell(cell, alpha=angle_alpha, beta=angle_beta, gamma=angle_gamma, abc=abc)
     431              :          ! COLUMNS       DATA TYPE      CONTENTS
     432              :          ! --------------------------------------------------
     433              :          !  1 -  6       Record name    "CRYST1"
     434              :          !  7 - 15       Real(9.3)      a (Angstroms)
     435              :          ! 16 - 24       Real(9.3)      b (Angstroms)
     436              :          ! 25 - 33       Real(9.3)      c (Angstroms)
     437              :          ! 34 - 40       Real(7.2)      alpha (degrees)
     438              :          ! 41 - 47       Real(7.2)      beta (degrees)
     439              :          ! 48 - 54       Real(7.2)      gamma (degrees)
     440              :          ! 56 - 66       LString        Space group
     441              :          ! 67 - 70       Integer        Z value
     442              :          WRITE (UNIT=iunit, FMT="(A6,3F9.3,3F7.2)") &
     443          236 :             "CRYST1", abc(1:3)*factor, angle_alpha, angle_beta, angle_gamma
     444           59 :          WRITE (UNIT=line(1:6), FMT="(A6)") "ATOM  "
     445         2999 :          DO iatom = 1, natom
     446         2940 :             line = ""
     447              :             ! COLUMNS        DATA TYPE       CONTENTS
     448              :             !  1 -  6        Record name     "ATOM  "
     449              :             !  7 - 11        Integer         Atom serial number
     450              :             ! 13 - 16        Atom            Atom name
     451              :             ! 17             Character       Alternate location indicator
     452              :             ! 18 - 20        Residue name    Residue name
     453              :             ! 22             Character       Chain identifier
     454              :             ! 23 - 26        Integer         Residue sequence number
     455              :             ! 27             AChar           Code for insertion of residues
     456              :             ! 31 - 38        Real(8.3)       Orthogonal coordinates for X in Angstrom
     457              :             ! 39 - 46        Real(8.3)       Orthogonal coordinates for Y in Angstrom
     458              :             ! 47 - 54        Real(8.3)       Orthogonal coordinates for Z in Angstrom
     459              :             ! 55 - 60        Real(6.2)       Occupancy
     460              :             ! 61 - 66        Real(6.2)       Temperature factor (Default = 0.0)
     461              :             ! 73 - 76        LString(4)      Segment identifier, left-justified
     462              :             ! 77 - 78        LString(2)      Element symbol, right-justified
     463              :             ! 79 - 80        LString(2)      Charge on the atom
     464              :             CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
     465              :                                  element_symbol=element_symbol, name=atm_name, &
     466         2940 :                                  fist_potential=fist_potential, shell=shell)
     467         2940 :             IF (LEN_TRIM(element_symbol) == 0) THEN
     468            0 :                dummy = qmmm_ff_precond_only_qm(id1=atm_name)
     469              :             END IF
     470         2940 :             name = TRIM(atm_name)
     471         2940 :             IF (ASSOCIATED(fist_potential)) THEN
     472         2940 :                CALL get_potential(potential=fist_potential, qeff=qeff)
     473              :             ELSE
     474            0 :                qeff = 0.0_dp
     475              :             END IF
     476         2940 :             IF (ASSOCIATED(shell)) CALL get_shell(shell=shell, charge=qeff)
     477         2940 :             WRITE (UNIT=line(1:6), FMT="(A6)") "ATOM  "
     478         2940 :             WRITE (UNIT=line(7:11), FMT="(I5)") MODULO(iatom, 100000)
     479         2940 :             WRITE (UNIT=line(13:16), FMT="(A4)") ADJUSTL(name)
     480              :             ! WRITE (UNIT=line(18:20),FMT="(A3)") TRIM(resname)
     481              :             ! WRITE (UNIT=line(23:26),FMT="(I4)") MODULO(idres,10000)
     482         5880 :             SELECT CASE (TRIM(content))
     483              :             CASE ("POS")
     484         2940 :                IF (PRESENT(array)) THEN
     485            0 :                   r(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
     486              :                ELSE
     487        11760 :                   r(:) = particle_set(iatom)%r(:)
     488              :                END IF
     489        11760 :                WRITE (UNIT=line(31:54), FMT="(3F8.3)") r(1:3)*factor
     490              :             CASE DEFAULT
     491         2940 :                CPABORT("PDB dump only for trajectory available")
     492              :             END SELECT
     493         2940 :             IF (my_charge_occup) THEN
     494         2130 :                WRITE (UNIT=line(55:60), FMT="(F6.2)") qeff
     495              :             ELSE
     496          810 :                WRITE (UNIT=line(55:60), FMT="(F6.2)") 0.0_dp
     497              :             END IF
     498         2940 :             IF (my_charge_beta) THEN
     499          480 :                WRITE (UNIT=line(61:66), FMT="(F6.2)") qeff
     500              :             ELSE
     501         2460 :                WRITE (UNIT=line(61:66), FMT="(F6.2)") 0.0_dp
     502              :             END IF
     503              :             ! WRITE (UNIT=line(73:76),FMT="(A4)") ADJUSTL(TRIM(molname))
     504         2940 :             WRITE (UNIT=line(77:78), FMT="(A2)") ADJUSTR(TRIM(element_symbol))
     505         2940 :             IF (my_charge_extended) THEN
     506          330 :                WRITE (UNIT=line(81:), FMT="(SP,F0.8)") qeff
     507              :             END IF
     508         2999 :             WRITE (UNIT=iunit, FMT="(A)") TRIM(line)
     509              :          END DO
     510           59 :          WRITE (UNIT=iunit, FMT="(A)") "END"
     511              :       CASE DEFAULT
     512        27317 :          CPABORT("Illegal dump type")
     513              :       END SELECT
     514              : 
     515        27258 :       CALL timestop(handle)
     516              : 
     517        27258 :    END SUBROUTINE write_particle_coordinates
     518              : 
     519              : ! **************************************************************************************************
     520              : !> \brief   Write the atomic coordinates to the output unit.
     521              : !> \param particle_set ...
     522              : !> \param subsys_section ...
     523              : !> \param charges ...
     524              : !> \date    05.06.2000
     525              : !> \author  MK
     526              : !> \version 1.0
     527              : ! **************************************************************************************************
     528        10179 :    SUBROUTINE write_fist_particle_coordinates(particle_set, subsys_section, charges)
     529              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     530              :       TYPE(section_vals_type), POINTER                   :: subsys_section
     531              :       REAL(KIND=dp), DIMENSION(:), OPTIONAL              :: charges
     532              : 
     533              :       CHARACTER(LEN=default_string_length)               :: name, unit_str
     534              :       INTEGER                                            :: iatom, ikind, iw, natom
     535              :       REAL(KIND=dp)                                      :: conv, mass, qcore, qeff, qshell
     536              :       TYPE(cp_logger_type), POINTER                      :: logger
     537              :       TYPE(shell_kind_type), POINTER                     :: shell_kind
     538              : 
     539        10179 :       NULLIFY (logger)
     540        10179 :       NULLIFY (shell_kind)
     541              : 
     542        10179 :       logger => cp_get_default_logger()
     543              :       iw = cp_print_key_unit_nr(logger, subsys_section, &
     544        10179 :                                 "PRINT%ATOMIC_COORDINATES", extension=".coordLog")
     545              : 
     546        10179 :       CALL section_vals_val_get(subsys_section, "PRINT%ATOMIC_COORDINATES%UNIT", c_val=unit_str)
     547        10179 :       conv = cp_unit_from_cp2k(1.0_dp, TRIM(unit_str))
     548        10179 :       CALL uppercase(unit_str)
     549        10179 :       IF (iw > 0) THEN
     550              :          WRITE (UNIT=iw, FMT="(/,/,T2,A)") &
     551         2504 :             "MODULE FIST: ATOMIC COORDINATES IN "//TRIM(unit_str)
     552              :          WRITE (UNIT=iw, FMT="(/,T4,A,T30,A,T44,A,T58,A,T66,A,T77,A)") &
     553         2504 :             "Atom Kind Name", "X", "Y", "Z", "q(eff)", "Mass"
     554         2504 :          natom = SIZE(particle_set)
     555       362909 :          DO iatom = 1, natom
     556              :             CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
     557              :                                  kind_number=ikind, &
     558              :                                  name=name, &
     559              :                                  mass=mass, &
     560              :                                  qeff=qeff, &
     561       360405 :                                  shell=shell_kind)
     562       360405 :             IF (PRESENT(charges)) qeff = charges(iatom)
     563       360405 :             IF (ASSOCIATED(shell_kind)) THEN
     564              :                CALL get_shell(shell=shell_kind, &
     565              :                               charge_core=qcore, &
     566         3426 :                               charge_shell=qshell)
     567         3426 :                qeff = qcore + qshell
     568              :             END IF
     569              :             WRITE (UNIT=iw, FMT="(T2,I6,1X,I4,1X,A7,3(1X,F13.6),2(1X,F8.4))") &
     570      1804529 :                iatom, ikind, name, particle_set(iatom)%r(1:3)*conv, qeff, mass/massunit
     571              :          END DO
     572         2504 :          WRITE (iw, "(A)") ""
     573              :       END IF
     574              : 
     575              :       CALL cp_print_key_finished_output(iw, logger, subsys_section, &
     576        10179 :                                         "PRINT%ATOMIC_COORDINATES")
     577              : 
     578        10179 :    END SUBROUTINE write_fist_particle_coordinates
     579              : 
     580              : ! **************************************************************************************************
     581              : !> \brief   Write the atomic coordinates to the output unit.
     582              : !> \param particle_set ...
     583              : !> \param qs_kind_set ...
     584              : !> \param subsys_section ...
     585              : !> \param label ...
     586              : !> \date    05.06.2000
     587              : !> \author  MK
     588              : !> \version 1.0
     589              : ! **************************************************************************************************
     590        20108 :    SUBROUTINE write_qs_particle_coordinates(particle_set, qs_kind_set, subsys_section, label)
     591              : 
     592              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     593              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     594              :       TYPE(section_vals_type), POINTER                   :: subsys_section
     595              :       CHARACTER(LEN=*), INTENT(IN)                       :: label
     596              : 
     597              :       CHARACTER(len=*), PARAMETER :: routineN = 'write_qs_particle_coordinates'
     598              : 
     599              :       CHARACTER(LEN=2)                                   :: element_symbol
     600              :       CHARACTER(LEN=default_string_length)               :: unit_str
     601              :       INTEGER                                            :: handle, iatom, ikind, iw, natom, z
     602              :       REAL(KIND=dp)                                      :: conv, mass, zeff
     603              :       TYPE(cp_logger_type), POINTER                      :: logger
     604              : 
     605        20108 :       CALL timeset(routineN, handle)
     606              : 
     607        20108 :       NULLIFY (logger)
     608        20108 :       logger => cp_get_default_logger()
     609              :       iw = cp_print_key_unit_nr(logger, subsys_section, &
     610        20108 :                                 "PRINT%ATOMIC_COORDINATES", extension=".coordLog")
     611              : 
     612        20108 :       CALL section_vals_val_get(subsys_section, "PRINT%ATOMIC_COORDINATES%UNIT", c_val=unit_str)
     613        20108 :       conv = cp_unit_from_cp2k(1.0_dp, TRIM(unit_str))
     614        20108 :       CALL uppercase(unit_str)
     615        20108 :       IF (iw > 0) THEN
     616              :          WRITE (UNIT=iw, FMT="(/,/,T2,A)") &
     617         4554 :             "MODULE "//TRIM(label)//": ATOMIC COORDINATES IN "//TRIM(unit_str)
     618              :          WRITE (UNIT=iw, FMT="(/,T4,A,T30,A,T44,A,T58,A,T66,A,T77,A)") &
     619         4554 :             "Atom Kind Element", "X", "Y", "Z", "Z(eff)", "Mass"
     620         4554 :          natom = SIZE(particle_set)
     621        26619 :          DO iatom = 1, natom
     622              :             CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
     623              :                                  kind_number=ikind, &
     624              :                                  element_symbol=element_symbol, &
     625              :                                  mass=mass, &
     626        22065 :                                  z=z)
     627        22065 :             CALL get_qs_kind(qs_kind_set(ikind), zeff=zeff)
     628              :             WRITE (UNIT=iw, FMT="(T2,I6,1X,I4,1X,A2,1X,I4,3(1X,F13.6),2(1X,F8.4))") &
     629        92814 :                iatom, ikind, element_symbol, z, particle_set(iatom)%r(1:3)*conv, zeff, mass/massunit
     630              :          END DO
     631         4554 :          WRITE (iw, "(A)") ""
     632              :       END IF
     633              : 
     634              :       CALL cp_print_key_finished_output(iw, logger, subsys_section, &
     635        20108 :                                         "PRINT%ATOMIC_COORDINATES")
     636              : 
     637        20108 :       CALL timestop(handle)
     638              : 
     639        20108 :    END SUBROUTINE write_qs_particle_coordinates
     640              : 
     641              : ! **************************************************************************************************
     642              : !> \brief   Write the matrix of the particle distances to the output unit.
     643              : !> \param particle_set ...
     644              : !> \param cell ...
     645              : !> \param subsys_section ...
     646              : !> \date    06.10.2000
     647              : !> \author  Matthias Krack
     648              : !> \version 1.0
     649              : ! **************************************************************************************************
     650        11703 :    SUBROUTINE write_particle_distances(particle_set, cell, subsys_section)
     651              : 
     652              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     653              :       TYPE(cell_type), POINTER                           :: cell
     654              :       TYPE(section_vals_type), POINTER                   :: subsys_section
     655              : 
     656              :       CHARACTER(len=*), PARAMETER :: routineN = 'write_particle_distances'
     657              : 
     658              :       CHARACTER(LEN=default_string_length)               :: unit_str
     659              :       INTEGER                                            :: handle, iatom, iw, jatom, natom
     660              :       INTEGER, DIMENSION(3)                              :: periodic
     661              :       LOGICAL                                            :: explicit
     662              :       REAL(KIND=dp)                                      :: conv, dab, dab_abort, dab_min, dab_warn
     663        11703 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: distance_matrix
     664              :       REAL(KIND=dp), DIMENSION(3)                        :: rab
     665              :       TYPE(cp_logger_type), POINTER                      :: logger
     666              : 
     667        11703 :       CALL timeset(routineN, handle)
     668              : 
     669        11703 :       CPASSERT(ASSOCIATED(particle_set))
     670        11703 :       CPASSERT(ASSOCIATED(cell))
     671        11703 :       CPASSERT(ASSOCIATED(subsys_section))
     672              : 
     673        11703 :       NULLIFY (logger)
     674        11703 :       logger => cp_get_default_logger()
     675              :       iw = cp_print_key_unit_nr(logger, subsys_section, &
     676        11703 :                                 "PRINT%INTERATOMIC_DISTANCES", extension=".distLog")
     677              : 
     678        11703 :       CALL section_vals_val_get(subsys_section, "PRINT%INTERATOMIC_DISTANCES%UNIT", c_val=unit_str)
     679        11703 :       conv = cp_unit_from_cp2k(1.0_dp, TRIM(unit_str))
     680              :       CALL section_vals_val_get(subsys_section, "PRINT%INTERATOMIC_DISTANCES%CHECK_INTERATOMIC_DISTANCES", &
     681        11703 :                                 r_val=dab_min, explicit=explicit)
     682              : 
     683        11703 :       dab_abort = 0.0_dp
     684        11703 :       dab_warn = 0.0_dp
     685        11703 :       natom = SIZE(particle_set)
     686              : 
     687              :       ! Compute interatomic distances only if their printout or check is explicitly requested
     688              :       ! Disable the default check for systems with more than 3000 atoms
     689        11703 :       IF (explicit .OR. (iw > 0) .OR. (natom <= 2000)) THEN
     690        11661 :          IF (dab_min > 0.0_dp) THEN
     691        11657 :             dab_warn = dab_min*conv
     692            4 :          ELSE IF (dab_min < 0.0_dp) THEN
     693            0 :             dab_abort = ABS(dab_min)*conv
     694              :          END IF
     695              :       END IF
     696              : 
     697        11703 :       IF ((iw > 0) .OR. (dab_abort > 0.0_dp) .OR. (dab_warn > 0.0_dp)) THEN
     698        11657 :          CALL get_cell(cell=cell, periodic=periodic)
     699        11657 :          IF (iw > 0) THEN
     700          132 :             ALLOCATE (distance_matrix(natom, natom))
     701           33 :             distance_matrix(:, :) = 0.0_dp
     702              :          END IF
     703       336200 :          DO iatom = 1, natom
     704    120470660 :             DO jatom = iatom + 1, natom
     705              :                rab(:) = pbc(particle_set(iatom)%r(:), &
     706    120134460 :                             particle_set(jatom)%r(:), cell)
     707    120134460 :                dab = SQRT(rab(1)*rab(1) + rab(2)*rab(2) + rab(3)*rab(3))*conv
     708    120134460 :                IF (dab_abort > 0.0_dp) THEN
     709              :                   ! Stop the run for interatomic distances smaller than the requested threshold
     710            0 :                   IF (dab < dab_abort) THEN
     711              :                      CALL cp_abort(__LOCATION__, "The distance between the atoms "// &
     712              :                                    TRIM(ADJUSTL(cp_to_string(iatom, fmt="(I8)")))//" and "// &
     713              :                                    TRIM(ADJUSTL(cp_to_string(jatom, fmt="(I8)")))//" is only "// &
     714              :                                    TRIM(ADJUSTL(cp_to_string(dab, fmt="(F6.3)")))//" "// &
     715              :                                    TRIM(ADJUSTL(unit_str))//" and thus smaller than the requested threshold of "// &
     716              :                                    TRIM(ADJUSTL(cp_to_string(dab_abort, fmt="(F6.3)")))//" "// &
     717            0 :                                    TRIM(ADJUSTL(unit_str)))
     718              :                   END IF
     719              :                END IF
     720    120134460 :                IF (dab < dab_warn) THEN
     721              :                   ! Print warning for interatomic distances smaller than the requested threshold
     722              :                   CALL cp_warn(__LOCATION__, "The distance between the atoms "// &
     723              :                                TRIM(ADJUSTL(cp_to_string(iatom, fmt="(I8)")))//" and "// &
     724              :                                TRIM(ADJUSTL(cp_to_string(jatom, fmt="(I8)")))//" is only "// &
     725              :                                TRIM(ADJUSTL(cp_to_string(dab, fmt="(F6.3)")))//" "// &
     726              :                                TRIM(ADJUSTL(unit_str))//" and thus smaller than the threshold of "// &
     727              :                                TRIM(ADJUSTL(cp_to_string(dab_warn, fmt="(F6.3)")))//" "// &
     728          912 :                                TRIM(ADJUSTL(unit_str)))
     729              :                END IF
     730    120459003 :                IF (iw > 0) THEN
     731        35186 :                   distance_matrix(iatom, jatom) = dab
     732        35186 :                   distance_matrix(jatom, iatom) = distance_matrix(iatom, jatom)
     733              :                END IF
     734              :             END DO
     735              :          END DO
     736        11657 :          IF (iw > 0) THEN
     737              :             ! Print the distance matrix
     738              :             WRITE (UNIT=iw, FMT="(/,/,T2,A)") &
     739           33 :                "INTERATOMIC DISTANCES IN "//TRIM(unit_str)
     740           33 :             CALL write_particle_matrix(distance_matrix, particle_set, iw)
     741           33 :             IF (ALLOCATED(distance_matrix)) DEALLOCATE (distance_matrix)
     742              :          END IF
     743              :          CALL cp_print_key_finished_output(iw, logger, subsys_section, &
     744        11657 :                                            "PRINT%INTERATOMIC_DISTANCES")
     745              :       END IF
     746              : 
     747        11703 :       CALL timestop(handle)
     748              : 
     749        11703 :    END SUBROUTINE write_particle_distances
     750              : 
     751              : ! **************************************************************************************************
     752              : !> \brief ...
     753              : !> \param matrix ...
     754              : !> \param particle_set ...
     755              : !> \param iw ...
     756              : !> \param el_per_part ...
     757              : !> \param Ilist ...
     758              : !> \param parts_per_line : number of particle columns to be printed in one line
     759              : ! **************************************************************************************************
     760           63 :    SUBROUTINE write_particle_matrix(matrix, particle_set, iw, el_per_part, Ilist, parts_per_line)
     761              :       REAL(KIND=dp), DIMENSION(:, :)                     :: matrix
     762              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     763              :       INTEGER, INTENT(IN)                                :: iw
     764              :       INTEGER, INTENT(IN), OPTIONAL                      :: el_per_part
     765              :       INTEGER, DIMENSION(:), OPTIONAL, POINTER           :: Ilist
     766              :       INTEGER, INTENT(IN), OPTIONAL                      :: parts_per_line
     767              : 
     768              :       CHARACTER(LEN=2)                                   :: element_symbol
     769              :       CHARACTER(LEN=default_string_length)               :: fmt_string1, fmt_string2
     770              :       INTEGER                                            :: from, i, iatom, icol, jatom, katom, &
     771              :                                                             my_el_per_part, my_parts_per_line, &
     772              :                                                             natom, to
     773           63 :       INTEGER, DIMENSION(:), POINTER                     :: my_list
     774              : 
     775           63 :       my_el_per_part = 1
     776           30 :       IF (PRESENT(el_per_part)) my_el_per_part = el_per_part
     777           63 :       my_parts_per_line = 5
     778           63 :       IF (PRESENT(parts_per_line)) my_parts_per_line = MAX(parts_per_line, 1)
     779              :       WRITE (fmt_string1, FMT='(A,I0,A)') &
     780           63 :          "(/,T2,9X,", my_parts_per_line, "(4X,I6,4X))"
     781              :       WRITE (fmt_string2, FMT='(A,I0,A)') &
     782           63 :          "(T2,I5,1X,A2,1X,", my_parts_per_line, "(1X,ES13.6E2))"
     783           63 :       IF (PRESENT(Ilist)) THEN
     784           30 :          natom = SIZE(Ilist)
     785              :       ELSE
     786           33 :          natom = SIZE(particle_set)
     787              :       END IF
     788          189 :       ALLOCATE (my_list(natom))
     789           63 :       IF (PRESENT(Ilist)) THEN
     790          180 :          my_list = Ilist
     791              :       ELSE
     792          927 :          DO i = 1, natom
     793          927 :             my_list(i) = i
     794              :          END DO
     795              :       END IF
     796           63 :       natom = natom*my_el_per_part
     797          317 :       DO jatom = 1, natom, my_parts_per_line
     798          254 :          from = jatom
     799          254 :          to = MIN(from + my_parts_per_line - 1, natom)
     800         1373 :          WRITE (UNIT=iw, FMT=TRIM(fmt_string1)) (icol, icol=from, to)
     801        15622 :          DO iatom = 1, natom
     802        15305 :             katom = iatom/my_el_per_part
     803        15305 :             IF (MOD(iatom, my_el_per_part) /= 0) katom = katom + 1
     804              :             CALL get_atomic_kind(atomic_kind=particle_set(my_list(katom))%atomic_kind, &
     805        15305 :                                  element_symbol=element_symbol)
     806              :             WRITE (UNIT=iw, FMT=TRIM(fmt_string2)) &
     807        15305 :                iatom, element_symbol, &
     808        30864 :                (matrix(iatom, icol), icol=from, to)
     809              :          END DO
     810              :       END DO
     811              : 
     812           63 :       DEALLOCATE (my_list)
     813              : 
     814           63 :    END SUBROUTINE write_particle_matrix
     815              : 
     816              : ! **************************************************************************************************
     817              : !> \brief   Write structure data requested by a separate structure data input
     818              : !>          section to the output unit.
     819              : !>          input_section can be either motion_section or subsys_section.
     820              : !>
     821              : !> \param particle_set ...
     822              : !> \param cell ...
     823              : !> \param input_section ...
     824              : !> \date    11.03.04
     825              : !> \par History
     826              : !>          Recovered (23.03.06,MK)
     827              : !> \author  MK
     828              : !> \version 1.0
     829              : ! **************************************************************************************************
     830        63087 :    SUBROUTINE write_structure_data(particle_set, cell, input_section)
     831              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     832              :       TYPE(cell_type), POINTER                           :: cell
     833              :       TYPE(section_vals_type), POINTER                   :: input_section
     834              : 
     835              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'write_structure_data'
     836              : 
     837              :       CHARACTER(LEN=default_string_length)               :: string, unit_str
     838              :       INTEGER                                            :: handle, i, i_rep, iw, n, n_rep, n_vals, &
     839              :                                                             natom, new_size, old_size, wrk2(2), &
     840              :                                                             wrk3(3), wrk4(4)
     841        63087 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: work
     842        63087 :       INTEGER, DIMENSION(:), POINTER                     :: atomic_indices, index_list
     843              :       LOGICAL                                            :: unique
     844              :       REAL(KIND=dp)                                      :: conv, dab
     845              :       REAL(KIND=dp), DIMENSION(3)                        :: r, rab, rbc, rcd, s
     846              :       TYPE(cp_logger_type), POINTER                      :: logger
     847              :       TYPE(section_vals_type), POINTER                   :: section
     848              : 
     849        63087 :       CALL timeset(routineN, handle)
     850        63087 :       NULLIFY (atomic_indices)
     851        63087 :       NULLIFY (index_list)
     852        63087 :       NULLIFY (logger)
     853        63087 :       NULLIFY (section)
     854        63087 :       string = ""
     855              : 
     856        63087 :       logger => cp_get_default_logger()
     857              :       iw = cp_print_key_unit_nr(logger=logger, &
     858              :                                 basis_section=input_section, &
     859              :                                 print_key_path="PRINT%STRUCTURE_DATA", &
     860        63087 :                                 extension=".coordLog")
     861              : 
     862        63087 :       CALL section_vals_val_get(input_section, "PRINT%STRUCTURE_DATA%UNIT", c_val=unit_str)
     863        63087 :       conv = cp_unit_from_cp2k(1.0_dp, TRIM(unit_str))
     864        63087 :       CALL uppercase(unit_str)
     865        63087 :       IF (iw > 0) THEN
     866          569 :          natom = SIZE(particle_set)
     867              :          section => section_vals_get_subs_vals(section_vals=input_section, &
     868          569 :                                                subsection_name="PRINT%STRUCTURE_DATA")
     869              : 
     870          569 :          WRITE (UNIT=iw, FMT="(/,T2,A)") "REQUESTED STRUCTURE DATA"
     871              :          ! Print the requested atomic position vectors
     872              :          CALL section_vals_val_get(section_vals=section, &
     873              :                                    keyword_name="POSITION", &
     874          569 :                                    n_rep_val=n_rep)
     875          569 :          IF (n_rep > 0) THEN
     876              :             WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
     877          145 :                "Position vectors r(i) of the atoms i in "//TRIM(unit_str)
     878          145 :             old_size = 0
     879          848 :             DO i_rep = 1, n_rep
     880              :                CALL section_vals_val_get(section_vals=section, &
     881              :                                          keyword_name="POSITION", &
     882              :                                          i_rep_val=i_rep, &
     883          703 :                                          i_vals=atomic_indices)
     884          703 :                n_vals = SIZE(atomic_indices)
     885          703 :                new_size = old_size + n_vals
     886          703 :                CALL reallocate(index_list, 1, new_size)
     887         2903 :                index_list(old_size + 1:new_size) = atomic_indices(1:n_vals)
     888          848 :                old_size = new_size
     889              :             END DO
     890          435 :             ALLOCATE (work(new_size))
     891          145 :             CALL sort(index_list, new_size, work)
     892          145 :             DEALLOCATE (work)
     893         1245 :             DO i = 1, new_size
     894         1100 :                WRITE (UNIT=string, FMT="(A,I0,A)") "(", index_list(i), ")"
     895         1100 :                IF ((index_list(i) < 1) .OR. (index_list(i) > natom)) THEN
     896              :                   WRITE (UNIT=iw, FMT="(T3,A)") &
     897           30 :                      "Invalid atomic index "//TRIM(string)//" specified. Print request is ignored."
     898           30 :                   CYCLE
     899              :                END IF
     900         1070 :                IF (i > 1) THEN
     901              :                   ! Skip redundant indices
     902          935 :                   IF (index_list(i) == index_list(i - 1)) CYCLE
     903              :                END IF
     904              :                WRITE (UNIT=iw, FMT="(T3,A,T20,A,3F13.6)") &
     905         4425 :                   "r"//TRIM(string), "=", pbc(particle_set(index_list(i))%r(1:3), cell)*conv
     906              :             END DO
     907          145 :             DEALLOCATE (index_list)
     908              :          END IF
     909              : 
     910              :          ! Print the requested atomic position vectors in scaled coordinates
     911              :          CALL section_vals_val_get(section_vals=section, &
     912              :                                    keyword_name="POSITION_SCALED", &
     913          569 :                                    n_rep_val=n_rep)
     914          569 :          IF (n_rep > 0) THEN
     915              :             WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
     916           27 :                "Position vectors s(i) of the atoms i in scaled coordinates"
     917           27 :             old_size = 0
     918           84 :             DO i_rep = 1, n_rep
     919              :                CALL section_vals_val_get(section_vals=section, &
     920              :                                          keyword_name="POSITION_SCALED", &
     921              :                                          i_rep_val=i_rep, &
     922           57 :                                          i_vals=atomic_indices)
     923           57 :                n_vals = SIZE(atomic_indices)
     924           57 :                new_size = old_size + n_vals
     925           57 :                CALL reallocate(index_list, 1, new_size)
     926          965 :                index_list(old_size + 1:new_size) = atomic_indices(1:n_vals)
     927           84 :                old_size = new_size
     928              :             END DO
     929           81 :             ALLOCATE (work(new_size))
     930           27 :             CALL sort(index_list, new_size, work)
     931           27 :             DEALLOCATE (work)
     932          481 :             DO i = 1, new_size
     933          454 :                WRITE (UNIT=string, FMT="(A,I0,A)") "(", index_list(i), ")"
     934          454 :                IF ((index_list(i) < 1) .OR. (index_list(i) > natom)) THEN
     935              :                   WRITE (UNIT=iw, FMT="(T3,A)") &
     936           30 :                      "Invalid atomic index "//TRIM(string)//" specified. Print request is ignored."
     937           30 :                   CYCLE
     938              :                END IF
     939          424 :                IF (i > 1) THEN
     940              :                   ! Skip redundant indices
     941          407 :                   IF (index_list(i) == index_list(i - 1)) CYCLE
     942              :                END IF
     943          424 :                r(1:3) = pbc(particle_set(index_list(i))%r(1:3), cell)
     944          424 :                CALL real_to_scaled(s, r, cell)
     945              :                WRITE (UNIT=iw, FMT="(T3,A,T20,A,3F13.6)") &
     946          451 :                   "s"//TRIM(string), "=", s(1:3)
     947              :             END DO
     948           27 :             DEALLOCATE (index_list)
     949              :          END IF
     950              : 
     951              :          ! Print the requested distances
     952              :          CALL section_vals_val_get(section_vals=section, &
     953              :                                    keyword_name="DISTANCE", &
     954          569 :                                    n_rep_val=n)
     955          569 :          IF (n > 0) THEN
     956              :             WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
     957              :                "Distance vector r(i,j) between the atom i and j in "// &
     958          129 :                TRIM(unit_str)
     959          355 :             DO i = 1, n
     960              :                CALL section_vals_val_get(section_vals=section, &
     961              :                                          keyword_name="DISTANCE", &
     962              :                                          i_rep_val=i, &
     963          226 :                                          i_vals=atomic_indices)
     964          226 :                string = ""
     965              :                WRITE (UNIT=string, FMT="(A,2(I0,A))") &
     966          226 :                   "(", atomic_indices(1), ",", atomic_indices(2), ")"
     967          678 :                wrk2 = atomic_indices
     968          226 :                CALL sort_unique(wrk2, unique)
     969          355 :                IF (((wrk2(1) >= 1) .AND. (wrk2(SIZE(wrk2)) <= natom)) .AND. unique) THEN
     970              :                   rab(:) = pbc(particle_set(atomic_indices(1))%r(:), &
     971          226 :                                particle_set(atomic_indices(2))%r(:), cell)
     972          226 :                   dab = SQRT(rab(1)*rab(1) + rab(2)*rab(2) + rab(3)*rab(3))
     973              :                   WRITE (UNIT=iw, FMT="(T3,A,T20,A,3F13.6,3X,A,F13.6)") &
     974          904 :                      "r"//TRIM(string), "=", rab(:)*conv, &
     975          452 :                      "|r| =", dab*conv
     976              :                ELSE
     977              :                   WRITE (UNIT=iw, FMT="(T3,A)") &
     978            0 :                      "Invalid atomic indices "//TRIM(string)//" specified. Print request is ignored."
     979              :                END IF
     980              :             END DO
     981              :          END IF
     982              : 
     983              :          ! Print the requested angles
     984              :          CALL section_vals_val_get(section_vals=section, &
     985              :                                    keyword_name="ANGLE", &
     986          569 :                                    n_rep_val=n)
     987          569 :          IF (n > 0) THEN
     988              :             WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
     989              :                "Angle a(i,j,k) between the atomic distance vectors r(j,i) and "// &
     990           67 :                "r(j,k) in DEGREE"
     991          139 :             DO i = 1, n
     992              :                CALL section_vals_val_get(section_vals=section, &
     993              :                                          keyword_name="ANGLE", &
     994              :                                          i_rep_val=i, &
     995           72 :                                          i_vals=atomic_indices)
     996           72 :                string = ""
     997              :                WRITE (UNIT=string, FMT="(A,3(I0,A))") &
     998           72 :                   "(", atomic_indices(1), ",", atomic_indices(2), ",", atomic_indices(3), ")"
     999          288 :                wrk3 = atomic_indices
    1000           72 :                CALL sort_unique(wrk3, unique)
    1001          139 :                IF (((wrk3(1) >= 1) .AND. (wrk3(SIZE(wrk3)) <= natom)) .AND. unique) THEN
    1002              :                   rab(:) = pbc(particle_set(atomic_indices(1))%r(:), &
    1003           67 :                                particle_set(atomic_indices(2))%r(:), cell)
    1004              :                   rbc(:) = pbc(particle_set(atomic_indices(2))%r(:), &
    1005           67 :                                particle_set(atomic_indices(3))%r(:), cell)
    1006              :                   WRITE (UNIT=iw, FMT="(T3,A,T26,A,F9.3)") &
    1007          268 :                      "a"//TRIM(string), "=", angle(-rab, rbc)*degree
    1008              :                ELSE
    1009              :                   WRITE (UNIT=iw, FMT="(T3,A)") &
    1010            5 :                      "Invalid atomic indices "//TRIM(string)//" specified. Print request is ignored."
    1011              :                END IF
    1012              :             END DO
    1013              :          END IF
    1014              : 
    1015              :          ! Print the requested dihedral angles
    1016              :          CALL section_vals_val_get(section_vals=section, &
    1017              :                                    keyword_name="DIHEDRAL_ANGLE", &
    1018          569 :                                    n_rep_val=n)
    1019          569 :          IF (n > 0) THEN
    1020              :             WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
    1021              :                "Dihedral angle d(i,j,k,l) between the planes (i,j,k) and (j,k,l) "// &
    1022            6 :                "in DEGREE"
    1023           17 :             DO i = 1, n
    1024              :                CALL section_vals_val_get(section_vals=section, &
    1025              :                                          keyword_name="DIHEDRAL_ANGLE", &
    1026              :                                          i_rep_val=i, &
    1027           11 :                                          i_vals=atomic_indices)
    1028           11 :                string = ""
    1029              :                WRITE (UNIT=string, FMT="(A,4(I0,A))") &
    1030           11 :                   "(", atomic_indices(1), ",", atomic_indices(2), ",", &
    1031           22 :                   atomic_indices(3), ",", atomic_indices(4), ")"
    1032           55 :                wrk4 = atomic_indices
    1033           11 :                CALL sort_unique(wrk4, unique)
    1034           17 :                IF (((wrk4(1) >= 1) .AND. (wrk4(SIZE(wrk4)) <= natom)) .AND. unique) THEN
    1035              :                   rab(:) = pbc(particle_set(atomic_indices(1))%r(:), &
    1036            1 :                                particle_set(atomic_indices(2))%r(:), cell)
    1037              :                   rbc(:) = pbc(particle_set(atomic_indices(2))%r(:), &
    1038            1 :                                particle_set(atomic_indices(3))%r(:), cell)
    1039              :                   rcd(:) = pbc(particle_set(atomic_indices(3))%r(:), &
    1040            1 :                                particle_set(atomic_indices(4))%r(:), cell)
    1041              :                   ! Match the sign convention of the TORSION collective variable.
    1042              :                   WRITE (UNIT=iw, FMT="(T3,A,T26,A,F9.3)") &
    1043            1 :                      "d"//TRIM(string), "=", -dihedral_angle(rab, rbc, rcd)*degree
    1044              :                ELSE
    1045              :                   WRITE (UNIT=iw, FMT="(T3,A)") &
    1046           10 :                      "Invalid atomic indices "//TRIM(string)//" specified. Print request is ignored."
    1047              :                END IF
    1048              :             END DO
    1049              :          END IF
    1050              :       END IF
    1051              :       CALL cp_print_key_finished_output(iw, logger, input_section, &
    1052        63087 :                                         "PRINT%STRUCTURE_DATA")
    1053              : 
    1054        63087 :       CALL timestop(handle)
    1055              : 
    1056        63087 :    END SUBROUTINE write_structure_data
    1057              : 
    1058              : ! **************************************************************************************************
    1059              : !> \brief   Write the final geometry and cell information to files
    1060              : !> \param particle_set pointer to particles with atm_name, element_symbol and position
    1061              : !> \param cell pointer to cell with abc, angle_alpha, angle_beta, angle_gamma and deth
    1062              : !> \param input_section pointer to motion_section which has PRINT%FINAL_STRUCTURE
    1063              : !> \param conv flag for whether convergence is achieved or not in optimization
    1064              : !> \param keep_angles flag for whether cell optimization keeps initial angles
    1065              : !> \param keep_symmetry flag for whether cell optimization keeps initial symmetry
    1066              : !> \param keep_volume flag for whether cell optimization keeps initial volume
    1067              : !> \param gopt_env_label the geometry optimization label "GEO_OPT", "CELL_OPT", ...
    1068              : !> \param constraint_label label for directions with constraint in cell optimization
    1069              : !> \par     Intended to be invoked in gopt_f_methods:write_final_info.
    1070              : !>          This implementation does not consider higher space groups even if
    1071              : !>          one is detected, and the chemical formulae are neither written in
    1072              : !>          the sorted "Hill notation" nor expressed in groups of molecules.
    1073              : !>          Other potentially useful but yet to be written information includes:
    1074              : !>          the external pressure from CELL_OPT/EXTERNAL_POTENTIAL and the
    1075              : !>          stress tensor (virial) for CELL_OPT;
    1076              : !>          the fixed atoms from MOTION/CONSTRAINT/FIXED_ATOMS for all.
    1077              : !>
    1078              : !>          History
    1079              : !>          04.2026 - Created as write_final_cif
    1080              : !>          05.2026 - Generalized to write_final_structure and enable extxyz
    1081              : !>          06.2026 - Adopted write_particle_coordinates for handling kind in extxyz
    1082              : !> \author  HE Zilong
    1083              : !> \version 1.0
    1084              : ! **************************************************************************************************
    1085         1087 :    SUBROUTINE write_final_structure(particle_set, cell, input_section, conv, &
    1086              :                                     keep_angles, keep_symmetry, keep_volume, &
    1087              :                                     gopt_env_label, constraint_label)
    1088              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
    1089              :       TYPE(cell_type), INTENT(IN), POINTER               :: cell
    1090              :       TYPE(section_vals_type), INTENT(IN), POINTER       :: input_section
    1091              :       LOGICAL, INTENT(IN)                                :: conv, keep_angles, keep_symmetry, &
    1092              :                                                             keep_volume
    1093              :       CHARACTER(LEN=default_string_length), INTENT(IN)   :: gopt_env_label
    1094              :       CHARACTER(LEN=4), INTENT(IN)                       :: constraint_label
    1095              : 
    1096              :       CHARACTER(len=*), PARAMETER :: routineN = 'write_final_structure'
    1097              : 
    1098              :       CHARACTER(LEN=1)                                   :: conv_str
    1099              :       CHARACTER(LEN=2)                                   :: element_symbol
    1100         1087 :       CHARACTER(LEN=2), ALLOCATABLE                      :: element_list(:)
    1101              :       CHARACTER(LEN=5)                                   :: pbc_str
    1102         1087 :       CHARACTER(LEN=:), ALLOCATABLE                      :: formula_structural, formula_sum
    1103              :       CHARACTER(LEN=default_path_length)                 :: cell_str, record, title
    1104              :       CHARACTER(LEN=default_string_length)               :: atm_name, f_cif, f_cif_label, &
    1105              :                                                             f_cif_type_symbol
    1106         1087 :       CHARACTER(LEN=default_string_length), ALLOCATABLE  :: cif_label(:), cif_type_symbol(:)
    1107              :       CHARACTER(LEN=timestamp_length)                    :: timestamp
    1108              :       INTEGER :: elem_seen, file_unit, gcd_all, handle, i, iatom, ielem, natom, output_unit, &
    1109              :          symmetry_id, w_cif_label, w_cif_type_symbol
    1110         1087 :       INTEGER, ALLOCATABLE                               :: count_list(:)
    1111              :       LOGICAL                                            :: dummy, elem_in_list, orthorhombic, &
    1112              :                                                             print_final_structure, print_kind, &
    1113              :                                                             write_cif, write_xyz
    1114              :       REAL(KIND=dp)                                      :: angle_alpha, angle_beta, angle_gamma, &
    1115              :                                                             deth, unit_conv
    1116              :       REAL(KIND=dp), DIMENSION(3)                        :: abc, r, s
    1117              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: hmat
    1118              :       TYPE(cp_logger_type), POINTER                      :: logger
    1119              :       TYPE(enumeration_type), POINTER                    :: enum
    1120              :       TYPE(keyword_type), POINTER                        :: symmetry_keyword
    1121              :       TYPE(section_type), POINTER                        :: tmp_cell_section
    1122              :       TYPE(section_vals_type), POINTER                   :: print_key
    1123              : 
    1124         1087 :       CALL timeset(routineN, handle)
    1125              : 
    1126         1087 :       NULLIFY (enum, logger, symmetry_keyword, print_key, tmp_cell_section)
    1127         1087 :       logger => cp_get_default_logger()
    1128         1087 :       output_unit = cp_logger_get_default_io_unit(logger)
    1129         1087 :       print_key => section_vals_get_subs_vals(input_section, "PRINT%FINAL_STRUCTURE")
    1130         1087 :       print_final_structure = BTEST(cp_print_key_should_output(logger%iter_info, print_key), cp_p_file)
    1131         1087 :       file_unit = -1
    1132         1087 :       conv_str = "F"
    1133         1087 :       IF (conv) conv_str(1:1) = "T"
    1134              : 
    1135              :       ! Collect cell information
    1136         1087 :       pbc_str = "F F F"
    1137              :       CALL get_cell(cell, alpha=angle_alpha, beta=angle_beta, gamma=angle_gamma, &
    1138              :                     deth=deth, orthorhombic=orthorhombic, abc=abc, h=hmat, &
    1139         1087 :                     symmetry_id=symmetry_id)
    1140         1087 :       IF (cell%perd(1) == 1) pbc_str(1:1) = "T"
    1141         1087 :       IF (cell%perd(2) == 1) pbc_str(3:3) = "T"
    1142         1087 :       IF (cell%perd(3) == 1) pbc_str(5:5) = "T"
    1143         1087 :       CALL create_cell_section(tmp_cell_section)
    1144         1087 :       symmetry_keyword => section_get_keyword(tmp_cell_section, "SYMMETRY")
    1145         1087 :       CALL keyword_get(symmetry_keyword, enum=enum)
    1146              :       ! cell_str is default_path_length which is longer
    1147              :       ! than default_string_length and should be enough
    1148              :       WRITE (UNIT=cell_str, FMT="(9(1X,F19.10))") &
    1149         1087 :          cp_unit_from_cp2k(hmat(1, 1), "angstrom"), &
    1150         1087 :          cp_unit_from_cp2k(hmat(2, 1), "angstrom"), &
    1151         1087 :          cp_unit_from_cp2k(hmat(3, 1), "angstrom"), &
    1152         1087 :          cp_unit_from_cp2k(hmat(1, 2), "angstrom"), &
    1153         1087 :          cp_unit_from_cp2k(hmat(2, 2), "angstrom"), &
    1154         1087 :          cp_unit_from_cp2k(hmat(3, 2), "angstrom"), &
    1155         1087 :          cp_unit_from_cp2k(hmat(1, 3), "angstrom"), &
    1156         1087 :          cp_unit_from_cp2k(hmat(2, 3), "angstrom"), &
    1157         2174 :          cp_unit_from_cp2k(hmat(3, 3), "angstrom")
    1158              : 
    1159              :       ! Collect atom information
    1160         1087 :       natom = SIZE(particle_set)
    1161         1087 :       ALLOCATE (element_list(nelem + 1), count_list(nelem + 1))
    1162         1087 :       count_list(:) = 0
    1163         4348 :       ALLOCATE (cif_label(natom), cif_type_symbol(natom))
    1164         1087 :       elem_seen = 0
    1165         1087 :       w_cif_type_symbol = 0
    1166         1087 :       w_cif_label = 0
    1167        37371 :       atom_loop: DO iatom = 1, natom
    1168        36284 :          elem_in_list = .FALSE.
    1169              :          CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
    1170        36284 :                               name=atm_name, element_symbol=element_symbol)
    1171        36284 :          cif_type_symbol(iatom) = TRIM(atm_name)
    1172              :          ! From write_particle_coordinates above it seems possible
    1173              :          ! for some atoms to have empty element symbols; whatever
    1174              :          ! these are, do not count them in the chemical formula
    1175        36284 :          IF (LEN_TRIM(element_symbol) == 0) THEN
    1176            0 :             dummy = qmmm_ff_precond_only_qm(id1=atm_name)
    1177            0 :             cif_label(iatom) = TRIM(atm_name)//TRIM(ADJUSTL(cp_to_string(iatom)))
    1178              :          ELSE
    1179        36284 :             cif_label(iatom) = TRIM(element_symbol)//TRIM(ADJUSTL(cp_to_string(iatom)))
    1180        60938 :             elem_loop: DO ielem = 1, elem_seen
    1181        60938 :                IF (element_list(ielem) == element_symbol) THEN
    1182        34199 :                   elem_in_list = .TRUE.
    1183        34199 :                   count_list(ielem) = count_list(ielem) + 1
    1184              :                   EXIT elem_loop
    1185              :                END IF
    1186              :             END DO elem_loop
    1187              :             IF (.NOT. elem_in_list) THEN
    1188         2085 :                elem_seen = elem_seen + 1
    1189         2085 :                element_list(elem_seen) = element_symbol
    1190         2085 :                count_list(elem_seen) = 1
    1191              :             END IF
    1192              :          END IF
    1193        36284 :          IF (LEN_TRIM(cif_type_symbol(iatom)) > w_cif_type_symbol) THEN
    1194              :             w_cif_type_symbol = LEN_TRIM(cif_type_symbol(iatom))
    1195              :          END IF
    1196        37371 :          IF (LEN_TRIM(cif_label(iatom)) > w_cif_label) THEN
    1197              :             w_cif_label = LEN_TRIM(cif_label(iatom))
    1198              :          END IF
    1199              :       END DO atom_loop
    1200              : 
    1201              :       ! Determine the format of each line in cif considering width of cif_type_symbol and cif_label
    1202              :       ! The fields are, in order:
    1203              :       !  _atom_site_type_symbol, _atom_site_label, _atom_site_symmetry_multiplicity,
    1204              :       !  _atom_site_fract_x, _atom_site_fract_y, _atom_site_fract_z, _atom_site_occupancy
    1205              :       ! in which:
    1206              :       !  _atom_site_type_symbol is taken as atm_name
    1207              :       !  _atom_site_label is taken as element_symbol//iatom
    1208              :       !  _atom_site_symmetry_multiplicity and _atom_site_occupancy are always 1
    1209         1087 :       f_cif_type_symbol = "A"//TRIM(ADJUSTL(cp_to_string(w_cif_type_symbol + 4)))
    1210         1087 :       f_cif_label = "A"//TRIM(ADJUSTL(cp_to_string(w_cif_label + 4)))
    1211         1087 :       f_cif = "(T3,"//TRIM(f_cif_type_symbol)//","//TRIM(f_cif_label)//",I4,3F14.8,F8.2)"
    1212              : 
    1213              :       ! Determine formula_sum
    1214         1087 :       CPASSERT(elem_seen > 0)
    1215         1087 :       CPASSERT(count_list(1) > 0)
    1216         1087 :       formula_sum = "'"
    1217         3172 :       DO ielem = 1, elem_seen
    1218         2085 :          formula_sum = formula_sum//TRIM(ADJUSTL(element_list(ielem)))
    1219         2085 :          formula_sum = formula_sum//TRIM(ADJUSTL(cp_to_string(count_list(ielem))))
    1220         3172 :          formula_sum = formula_sum//" "
    1221              :       END DO
    1222         1087 :       formula_sum = TRIM(ADJUSTL(formula_sum))//"'"
    1223              : 
    1224              :       ! Determine formula_structural and Z
    1225         1087 :       gcd_all = count_list(1)
    1226         3172 :       DO ielem = 1, elem_seen
    1227         3172 :          IF (count_list(ielem) /= 0) THEN
    1228         2085 :             gcd_all = gcd(gcd_all, count_list(ielem))
    1229              :          END IF
    1230              :       END DO
    1231        65228 :       IF (gcd_all > 1) count_list = count_list/gcd_all
    1232         1087 :       formula_structural = "'"
    1233         3172 :       DO ielem = 1, elem_seen
    1234         2085 :          formula_structural = formula_structural//TRIM(ADJUSTL(element_list(ielem)))
    1235         2085 :          formula_structural = formula_structural//TRIM(ADJUSTL(cp_to_string(count_list(ielem))))
    1236         3172 :          formula_structural = formula_structural//" "
    1237              :       END DO
    1238         1087 :       formula_structural = TRIM(ADJUSTL(formula_structural))//"'"
    1239              : 
    1240              :       ! Write XYZ
    1241         1087 :       CALL section_vals_val_get(print_key, "PRINT_XYZ", l_val=write_xyz)
    1242         1087 :       write_xyz = write_xyz .AND. print_final_structure
    1243         1087 :       CALL section_vals_val_get(print_key, "PRINT_ATOM_KIND", l_val=print_kind)
    1244         1087 :       IF (write_xyz) THEN
    1245              :          ! Print a message to log
    1246              :          record = cp_print_key_generate_filename(logger, print_key, &
    1247              :                                                  extension=".xyz", &
    1248         1060 :                                                  my_local=.FALSE.)
    1249         1060 :          IF (output_unit > 0) THEN
    1250          560 :             IF (conv) THEN
    1251              :                WRITE (UNIT=output_unit, FMT="(/,T2,A)") &
    1252          198 :                   routineN//": Optimization converged, writing XYZ file gladly:"
    1253              :             ELSE
    1254              :                WRITE (UNIT=output_unit, FMT="(/,T2,A)") &
    1255          362 :                   routineN//": Optimization not yet converged, writing XYZ file anyway:"
    1256              :             END IF
    1257          560 :             WRITE (UNIT=output_unit, FMT="(T3,A)") TRIM(record)
    1258              :          END IF
    1259              : 
    1260              :          ! Prepare title
    1261              :          WRITE (UNIT=title, FMT="(A)") &
    1262              :             'Lattice="'//TRIM(ADJUSTL(cell_str))//'" '// &
    1263              :             'Properties=species:S:1:pos:R:3 '// &
    1264              :             'pbc="'//pbc_str//'" '// &
    1265         1060 :             'Converged='//conv_str
    1266              :          ! Extended XYZ uses angstrom for positions
    1267         1060 :          unit_conv = cp_unit_from_cp2k(1.0_dp, "angstrom")
    1268              :          ! Prepare file unit and write to it
    1269              :          file_unit = cp_print_key_unit_nr(logger, input_section, "PRINT%FINAL_STRUCTURE", &
    1270              :                                           file_status="REPLACE", file_form="FORMATTED", &
    1271         1060 :                                           extension=".xyz")
    1272         1060 :          IF (file_unit > 0) THEN
    1273              :             CALL write_particle_coordinates(particle_set, file_unit, dump_extxyz, "POS", title, &
    1274          560 :                                             cell=cell, unit_conv=unit_conv, print_kind=print_kind)
    1275              :          END IF
    1276              :       END IF
    1277              : 
    1278              :       ! Write CIF
    1279         1087 :       CALL section_vals_val_get(print_key, "PRINT_CIF", l_val=write_cif)
    1280         1087 :       write_cif = write_cif .AND. print_final_structure
    1281         1087 :       IF (write_cif) THEN
    1282              :          ! Print a message to log
    1283              :          record = cp_print_key_generate_filename(logger, print_key, &
    1284              :                                                  extension=".cif", &
    1285         1060 :                                                  my_local=.FALSE.)
    1286         1060 :          IF (output_unit > 0) THEN
    1287          560 :             IF (conv) THEN
    1288              :                WRITE (UNIT=output_unit, FMT="(/,T2,A)") &
    1289          198 :                   routineN//": Optimization converged, writing CIF file gladly:"
    1290              :             ELSE
    1291              :                WRITE (UNIT=output_unit, FMT="(/,T2,A)") &
    1292          362 :                   routineN//": Optimization not yet converged, writing CIF file anyway:"
    1293              :             END IF
    1294          560 :             WRITE (UNIT=output_unit, FMT="(T3,A)") TRIM(record)
    1295              :          END IF
    1296              : 
    1297              :          ! Make timestamp for the file
    1298         1060 :          CALL m_timestamp(timestamp)
    1299              : 
    1300              :          ! Prepare file unit and write to it
    1301              :          file_unit = cp_print_key_unit_nr(logger, input_section, "PRINT%FINAL_STRUCTURE", &
    1302              :                                           file_status="REPLACE", file_form="FORMATTED", &
    1303         1060 :                                           extension=".cif")
    1304         1060 :          IF (file_unit > 0) THEN
    1305              :             ! Generic information
    1306              :             WRITE (UNIT=file_unit, FMT="(A)") &
    1307          560 :                "# CIF file created by CP2K "//TRIM(moduleN)//":"//TRIM(routineN)
    1308              :             WRITE (UNIT=file_unit, FMT="(A)") &
    1309          560 :                "data_"//TRIM(logger%iter_info%project_name)
    1310              :             WRITE (UNIT=file_unit, FMT="(A,T39,A)") &
    1311          560 :                "_audit_creation_date", timestamp(:10)
    1312              :             WRITE (UNIT=file_unit, FMT="(A,/,A,/,A)") &
    1313          560 :                "_audit_creation_method", ";", &
    1314         1120 :                TRIM(cp2k_version)//" (revision "//TRIM(compile_revision)//")"
    1315              :             WRITE (UNIT=file_unit, FMT="(A,/,A,/,A,/,A)") &
    1316          560 :                "Project name "//TRIM(logger%iter_info%project_name), &
    1317          560 :                "submitted by "//TRIM(r_user_name)//"@"//TRIM(r_host_name), &
    1318          560 :                "processed in "//TRIM(r_cwd), &
    1319         1120 :                "generated at "//TRIM(timestamp)
    1320              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1321          560 :                REPEAT(" -*WARNING*- ", 6)
    1322              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1323          560 :                "WARNING| the lines enclosed in between -*WARNING*- contain metadata"
    1324              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1325          560 :                "WARNING| provided in a tentative format for the newly implemented"
    1326              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1327          560 :                "WARNING| CIF output from CP2K version 2026.2, which may be unstable"
    1328              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1329          560 :                "WARNING| and subject to overhaul in a future release. Contact the"
    1330              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1331          560 :                "WARNING| developers in case a stabilized format is needed for some"
    1332              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1333          560 :                "WARNING| downstream CIF parser with text pattern matchers (regexp)."
    1334              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1335          560 :                "- Optimization type: "//TRIM(gopt_env_label)
    1336          560 :             IF (conv) THEN
    1337              :                WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1338          198 :                   "- Optimization converged: TRUE"
    1339              :             ELSE
    1340              :                WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1341          362 :                   "- Optimization converged: FALSE"
    1342              :             END IF
    1343              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1344          560 :                "- Requested initial cell symmetry: "//TRIM(enum_i2c(enum, symmetry_id))
    1345          560 :             IF (orthorhombic) THEN
    1346              :                WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1347          446 :                   "- Cell is numerically orthorhombic: TRUE"
    1348              :             ELSE
    1349              :                WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1350          114 :                   "- Cell is numerically orthorhombic: FALSE"
    1351              :             END IF
    1352              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1353          560 :                "- Periodicity of cell: "//TRIM(pbc_str)
    1354          560 :             IF (gopt_env_label == "CELL_OPT") THEN
    1355              :                WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1356          106 :                   "- Cell is subject to optimization: TRUE"
    1357              :                WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1358          106 :                   "- Cell has constraint on direction: "//TRIM(ADJUSTL(constraint_label))
    1359          106 :                IF (keep_angles) THEN
    1360              :                   WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1361           18 :                      "- Keep angles between the cell vectors during optimization: TRUE"
    1362              :                ELSE
    1363              :                   WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1364           88 :                      "- Keep angles between the cell vectors during optimization: FALSE"
    1365              :                END IF
    1366          106 :                IF (keep_symmetry) THEN
    1367              :                   WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1368           21 :                      "- Keep initial cell symmetry during optimization: TRUE"
    1369              :                ELSE
    1370              :                   WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1371           85 :                      "- Keep initial cell symmetry during optimization: FALSE"
    1372              :                END IF
    1373          106 :                IF (keep_volume) THEN
    1374              :                   WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1375            3 :                      "- Keep initial cell volume during optimization: TRUE"
    1376              :                ELSE
    1377              :                   WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1378          103 :                      "- Keep initial cell volume during optimization: FALSE"
    1379              :                END IF
    1380              :             ELSE
    1381              :                WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1382          454 :                   "- Cell is subject to optimization: FALSE"
    1383              :             END IF
    1384              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1385          560 :                "- Final cell vectors A, B, C by rows [angstrom]:"
    1386         2240 :             DO i = 1, 3
    1387              :                WRITE (UNIT=file_unit, FMT="(T3,3(1X,F19.10))") &
    1388         1680 :                   cp_unit_from_cp2k(hmat(1, i), "angstrom"), &
    1389         1680 :                   cp_unit_from_cp2k(hmat(2, i), "angstrom"), &
    1390         3920 :                   cp_unit_from_cp2k(hmat(3, i), "angstrom")
    1391              :             END DO
    1392              :             WRITE (UNIT=file_unit, FMT="(T2,A)") &
    1393          560 :                REPEAT("-*WARNING*- ", 6)
    1394          560 :             WRITE (UNIT=file_unit, FMT="(A)") ";"
    1395              :             ! Data of cell and geometry
    1396              :             WRITE (UNIT=file_unit, FMT="(/,A,T44,A)") &
    1397          560 :                "_symmetry_space_group_name_H-M", "'P 1'"
    1398              :             WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
    1399          560 :                "_cell_length_a", cp_unit_from_cp2k(abc(1), "angstrom")
    1400              :             WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
    1401          560 :                "_cell_length_b", cp_unit_from_cp2k(abc(2), "angstrom")
    1402              :             WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
    1403          560 :                "_cell_length_c", cp_unit_from_cp2k(abc(3), "angstrom")
    1404              :             WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
    1405          560 :                "_cell_angle_alpha", angle_alpha
    1406              :             WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
    1407          560 :                "_cell_angle_beta", angle_beta
    1408              :             WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
    1409          560 :                "_cell_angle_gamma", angle_gamma
    1410              :             WRITE (UNIT=file_unit, FMT="(A,T48,A)") &
    1411          560 :                "_symmetry_Int_Tables_number", "1"
    1412              :             WRITE (UNIT=file_unit, FMT="(A,T36,A)") &
    1413          560 :                "_chemical_formula_structural", formula_structural
    1414              :             WRITE (UNIT=file_unit, FMT="(A,T36,A)") &
    1415          560 :                "_chemical_formula_sum", formula_sum
    1416              :             WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
    1417          560 :                "_cell_volume", cp_unit_from_cp2k(ABS(deth), "angstrom^3")
    1418              :             WRITE (UNIT=file_unit, FMT="(A,T41,I8)") &
    1419          560 :                "_cell_formula_units_Z", gcd_all
    1420              :             WRITE (UNIT=file_unit, FMT="(A,/,T2,A,/,T2,A,/,T3,A)") &
    1421          560 :                "loop_", "_symmetry_equiv_pos_site_id", &
    1422         1120 :                "_symmetry_equiv_pos_as_xyz", "1  'x, y, z'"
    1423              :             WRITE (UNIT=file_unit, FMT="(A,/,T2,A,/,T2,A,/,T2,A,/,T2,A,/,T2,A,/,T2,A,/,T2,A)") &
    1424          560 :                "loop_", "_atom_site_type_symbol", "_atom_site_label", &
    1425          560 :                "_atom_site_symmetry_multiplicity", "_atom_site_fract_x", &
    1426         1120 :                "_atom_site_fract_y", "_atom_site_fract_z", "_atom_site_occupancy"
    1427        19077 :             DO iatom = 1, natom
    1428              :                ! positive_range=.TRUE. makes r(1:3) in [0, cell%hmat(i,i)] and
    1429              :                ! s(1:3) in [0, 1], so there is no need to MODULO s(1:3) by 1.0
    1430        18517 :                r(1:3) = pbc(particle_set(iatom)%r(1:3), cell, positive_range=.TRUE.)
    1431        18517 :                CALL real_to_scaled(s, r, cell)
    1432              :                WRITE (UNIT=file_unit, FMT=TRIM(f_cif)) &
    1433        19077 :                   cif_type_symbol(iatom), cif_label(iatom), 1, s(1:3), 1.0_dp
    1434              :             END DO
    1435              :          END IF
    1436              :       END IF
    1437              : 
    1438              :       ! Finish
    1439            0 :       DEALLOCATE (element_list, count_list, formula_structural, &
    1440         1087 :                   formula_sum, cif_label, cif_type_symbol)
    1441         1087 :       CALL section_release(tmp_cell_section)
    1442              :       CALL cp_print_key_finished_output(file_unit, logger, input_section, &
    1443         1087 :                                         "PRINT%FINAL_STRUCTURE")
    1444         1087 :       IF (output_unit > 0 .AND. (write_xyz .OR. write_cif)) THEN
    1445              :          WRITE (UNIT=output_unit, FMT='(/,T2,A)') &
    1446          560 :             routineN//": Done!"
    1447              :       END IF
    1448              : 
    1449         1087 :       CALL timestop(handle)
    1450              : 
    1451         5435 :    END SUBROUTINE write_final_structure
    1452              : 
    1453        14684 : END MODULE particle_methods
        

Generated by: LCOV version 2.0-1