LCOV - code coverage report
Current view: top level - src - molden_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:591cf04) Lines: 58.6 % 280 164
Test Date: 2026-09-21 02:17:57 Functions: 75.0 % 4 3

            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  Functions handling the MOLDEN format. Split from mode_selective.
      10              : !> \author Teodoro Laino, 03.2009
      11              : ! **************************************************************************************************
      12              : MODULE molden_utils
      13              :    USE admm_types,                      ONLY: admm_type
      14              :    USE admm_utils,                      ONLY: admm_correct_for_eigenvalues,&
      15              :                                               admm_uncorrect_for_eigenvalues
      16              :    USE atomic_kind_types,               ONLY: get_atomic_kind
      17              :    USE basis_set_types,                 ONLY: get_gto_basis_set,&
      18              :                                               gto_basis_set_type
      19              :    USE cell_types,                      ONLY: cell_type
      20              :    USE cp_array_utils,                  ONLY: cp_1d_r_p_type
      21              :    USE cp_control_types,                ONLY: dft_control_type
      22              :    USE cp_dbcsr_api,                    ONLY: dbcsr_p_type,&
      23              :                                               dbcsr_type
      24              :    USE cp_dbcsr_operations,             ONLY: copy_dbcsr_to_fm
      25              :    USE cp_fm_types,                     ONLY: cp_fm_get_info,&
      26              :                                               cp_fm_get_submatrix,&
      27              :                                               cp_fm_type
      28              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      29              :                                               cp_logger_type
      30              :    USE cp_output_handling,              ONLY: cp_p_file,&
      31              :                                               cp_print_key_finished_output,&
      32              :                                               cp_print_key_should_output,&
      33              :                                               cp_print_key_unit_nr
      34              :    USE input_constants,                 ONLY: gto_cartesian,&
      35              :                                               gto_spherical
      36              :    USE input_section_types,             ONLY: section_vals_type,&
      37              :                                               section_vals_val_get
      38              :    USE kinds,                           ONLY: dp
      39              :    USE mathconstants,                   ONLY: pi
      40              :    USE orbital_pointers,                ONLY: nco,&
      41              :                                               nso
      42              :    USE orbital_transformation_matrices, ONLY: orbtramat
      43              :    USE particle_types,                  ONLY: particle_type
      44              :    USE periodic_table,                  ONLY: get_ptable_info
      45              :    USE physcon,                         ONLY: angstrom,&
      46              :                                               massunit
      47              :    USE qs_environment_types,            ONLY: get_qs_env,&
      48              :                                               qs_environment_type
      49              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      50              :                                               get_qs_kind_set,&
      51              :                                               qs_kind_type
      52              :    USE qs_mo_methods,                   ONLY: calculate_subspace_eigenvalues
      53              :    USE qs_mo_types,                     ONLY: get_mo_set,&
      54              :                                               mo_set_type
      55              : #include "./base/base_uses.f90"
      56              : 
      57              :    IMPLICIT NONE
      58              : 
      59              :    PRIVATE
      60              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'molden_utils'
      61              :    LOGICAL, PARAMETER                   :: debug_this_module = .FALSE.
      62              : 
      63              :    INTEGER, PARAMETER                   :: molden_lmax = 4
      64              :    INTEGER, PARAMETER                   :: molden_ncomax = (molden_lmax + 1)*(molden_lmax + 2)/2 ! 15
      65              : 
      66              :    PUBLIC :: write_vibrations_molden, write_mos_molden
      67              : 
      68              : CONTAINS
      69              : 
      70              : ! **************************************************************************************************
      71              : !> \brief Write the CP2K [Cell] extension to a MOLDEN file
      72              : !> \param iw output unit
      73              : !> \param cell simulation cell
      74              : !> \param unit_choice 1 for atomic units, 2 for Angstrom
      75              : ! **************************************************************************************************
      76            0 :    SUBROUTINE write_cell_molden(iw, cell, unit_choice)
      77              :       INTEGER, INTENT(IN)                                :: iw
      78              :       TYPE(cell_type), INTENT(IN)                        :: cell
      79              :       INTEGER, INTENT(IN)                                :: unit_choice
      80              : 
      81              :       REAL(KIND=dp)                                      :: scale_factor
      82              : 
      83            0 :       IF (unit_choice == 2) THEN
      84            0 :          scale_factor = angstrom
      85            0 :          WRITE (iw, '(T2,A)') "[Cell] Angs"
      86              :       ELSE
      87            0 :          scale_factor = 1.0_dp
      88            0 :          WRITE (iw, '(T2,A)') "[Cell] AU"
      89              :       END IF
      90              :       WRITE (iw, '(T2,3(F12.6,3X))') &
      91            0 :          cell%hmat(1, 1)*scale_factor, cell%hmat(2, 1)*scale_factor, cell%hmat(3, 1)*scale_factor
      92              :       WRITE (iw, '(T2,3(F12.6,3X))') &
      93            0 :          cell%hmat(1, 2)*scale_factor, cell%hmat(2, 2)*scale_factor, cell%hmat(3, 2)*scale_factor
      94              :       WRITE (iw, '(T2,3(F12.6,3X))') &
      95            0 :          cell%hmat(1, 3)*scale_factor, cell%hmat(2, 3)*scale_factor, cell%hmat(3, 3)*scale_factor
      96            0 :    END SUBROUTINE write_cell_molden
      97              : 
      98              : ! **************************************************************************************************
      99              : !> \brief Write out the MOs in molden format for visualisation
     100              : !> \param mos the set of MOs (both spins, if UKS)
     101              : !> \param qs_kind_set for basis set info
     102              : !> \param particle_set particles data structure, for positions and kinds
     103              : !> \param print_section input section containing relevant print key
     104              : !> \param cell ...
     105              : !> \param unoccupied_orbs optional: unoccupied orbital coefficients from make_lumo_gpw
     106              : !> \param unoccupied_evals optional: unoccupied orbital eigenvalues
     107              : !> \param qs_env ...
     108              : !> \param calc_energies ...
     109              : !> \author MattW, IainB
     110              : ! **************************************************************************************************
     111        12021 :    SUBROUTINE write_mos_molden(mos, qs_kind_set, particle_set, print_section, cell, &
     112        12021 :                                unoccupied_orbs, unoccupied_evals, qs_env, calc_energies)
     113              :       TYPE(mo_set_type), DIMENSION(:), INTENT(IN)        :: mos
     114              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     115              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     116              :       TYPE(section_vals_type), POINTER                   :: print_section
     117              :       TYPE(cell_type), OPTIONAL, POINTER                 :: cell
     118              :       TYPE(cp_fm_type), DIMENSION(:), INTENT(IN), &
     119              :          OPTIONAL                                        :: unoccupied_orbs
     120              :       TYPE(cp_1d_r_p_type), DIMENSION(:), INTENT(IN), &
     121              :          OPTIONAL                                        :: unoccupied_evals
     122              :       TYPE(qs_environment_type), OPTIONAL, POINTER       :: qs_env
     123              :       LOGICAL, INTENT(IN), OPTIONAL                      :: calc_energies
     124              : 
     125              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'write_mos_molden'
     126              :       CHARACTER(LEN=molden_lmax+1), PARAMETER            :: angmom = "spdfg"
     127              : 
     128              :       CHARACTER(LEN=15)                                  :: fmtstr1, fmtstr2
     129              :       CHARACTER(LEN=2)                                   :: element_symbol
     130              :       INTEGER :: gto_kind, handle, i, iatom, icgf, icol, ikind, ipgf, irow, irow_in, iset, isgf, &
     131              :          ishell, ispin, iw, lshell, ncgf, ncol_global, ndigits, nrow_global, nset, nsgf, numos, &
     132              :          unit_choice, z
     133        12021 :       INTEGER, DIMENSION(:), POINTER                     :: npgf, nshell
     134        12021 :       INTEGER, DIMENSION(:, :), POINTER                  :: l
     135              :       INTEGER, DIMENSION(molden_ncomax, 0:molden_lmax)   :: orbmap
     136              :       LOGICAL                                            :: do_calc_energies, ghost_atom, &
     137              :                                                             mark_ghost, print_warn, write_cell, &
     138              :                                                             write_pseudo
     139              :       REAL(KIND=dp)                                      :: expzet, prefac, scale_factor, zeff
     140        12021 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: cmatrix, smatrix
     141        12021 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: mo_eigenvalues
     142        12021 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: zet
     143        12021 :       REAL(KIND=dp), DIMENSION(:, :, :), POINTER         :: gcc
     144              :       TYPE(admm_type), POINTER                           :: admm_env
     145              :       TYPE(cp_fm_type), POINTER                          :: mo_coeff
     146              :       TYPE(cp_logger_type), POINTER                      :: logger
     147        12021 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: ks
     148              :       TYPE(dbcsr_type), POINTER                          :: matrix_ks, mo_coeff_deriv
     149              :       TYPE(dft_control_type), POINTER                    :: dft_control
     150              :       TYPE(gto_basis_set_type), POINTER                  :: orb_basis_set
     151              : 
     152        12021 :       CALL timeset(routineN, handle)
     153              : 
     154        12021 :       logger => cp_get_default_logger()
     155        12021 :       IF (BTEST(cp_print_key_should_output(logger%iter_info, print_section, ""), cp_p_file)) THEN
     156              : 
     157              :          iw = cp_print_key_unit_nr(logger, print_section, "", &
     158           22 :                                    extension=".molden", file_status='REPLACE')
     159              : 
     160           22 :          print_warn = .TRUE.
     161              : 
     162           22 :          CALL section_vals_val_get(print_section, "UNIT", i_val=unit_choice)
     163           22 :          IF (unit_choice == 2) THEN
     164              :             scale_factor = angstrom
     165              :          ELSE
     166           22 :             scale_factor = 1.0_dp
     167              :          END IF
     168              : 
     169           22 :          CALL section_vals_val_get(print_section, "NDIGITS", i_val=ndigits)
     170           22 :          ndigits = MIN(MAX(3, ndigits), 30)
     171           22 :          WRITE (UNIT=fmtstr1, FMT='("(I6,1X,ES",I0,".",I0,")")') ndigits + 7, ndigits
     172           22 :          WRITE (UNIT=fmtstr2, FMT='("((T51,2F",I0,".",I0,"))")') ndigits + 10, ndigits
     173              : 
     174           22 :          CALL section_vals_val_get(print_section, "GTO_KIND", i_val=gto_kind)
     175           22 :          CALL section_vals_val_get(print_section, "WRITE_CELL", l_val=write_cell)
     176           22 :          CALL section_vals_val_get(print_section, "WRITE_PSEUDO", l_val=write_pseudo)
     177           22 :          CALL section_vals_val_get(print_section, "MARK_GHOST", l_val=mark_ghost)
     178              : 
     179           22 :          IF (mos(1)%use_mo_coeff_b) THEN
     180              :             ! we are using the dbcsr mo_coeff
     181              :             ! we copy it to the fm anyway
     182            0 :             DO ispin = 1, SIZE(mos)
     183            0 :                CPASSERT(ASSOCIATED(mos(ispin)%mo_coeff_b))
     184              :                CALL copy_dbcsr_to_fm(mos(ispin)%mo_coeff_b, &
     185            0 :                                      mos(ispin)%mo_coeff) !fm->dbcsr
     186              :             END DO
     187              :          END IF
     188              : 
     189           22 :          IF (iw > 0) THEN
     190           11 :             WRITE (iw, '(T2,A)') "[Molden Format]"
     191           11 :             IF (write_cell) THEN
     192            0 :                CPASSERT(PRESENT(cell))
     193            0 :                CPASSERT(ASSOCIATED(cell))
     194            0 :                CALL write_cell_molden(iw, cell, unit_choice)
     195              :             END IF
     196           11 :             IF (unit_choice == 2) THEN
     197            0 :                WRITE (iw, '(T2,A)') "[Atoms] Angs"
     198              :             ELSE
     199           11 :                WRITE (iw, '(T2,A)') "[Atoms] AU"
     200              :             END IF
     201          144 :             DO i = 1, SIZE(particle_set)
     202              :                CALL get_atomic_kind(atomic_kind=particle_set(i)%atomic_kind, kind_number=ikind, &
     203          133 :                                     element_symbol=element_symbol)
     204          133 :                CALL get_ptable_info(element_symbol, number=z)
     205          133 :                IF (mark_ghost) THEN
     206            0 :                   CALL get_qs_kind(qs_kind_set(ikind), ghost=ghost_atom)
     207            0 :                   IF (ghost_atom) z = 0
     208              :                END IF
     209              : 
     210              :                WRITE (iw, '(T2,A2,I6,I6,3X,3(F12.6,3X))') &
     211          676 :                   element_symbol, i, z, particle_set(i)%r(:)*scale_factor
     212              :             END DO
     213           11 :             IF (write_pseudo) THEN
     214            0 :                WRITE (iw, '(T2,A)') "[Pseudo]"
     215            0 :                DO i = 1, SIZE(particle_set)
     216              :                   CALL get_atomic_kind(atomic_kind=particle_set(i)%atomic_kind, kind_number=ikind, &
     217            0 :                                        element_symbol=element_symbol)
     218            0 :                   CALL get_qs_kind(qs_kind_set(ikind), zeff=zeff)
     219              :                   WRITE (iw, '(T2,A2,I6,I6)') &
     220            0 :                      element_symbol, i, NINT(zeff)
     221              :                END DO
     222              :             END IF
     223              : 
     224           11 :             WRITE (iw, '(T2,A)') "[GTO]"
     225              : 
     226          144 :             DO i = 1, SIZE(particle_set)
     227              :                CALL get_atomic_kind(atomic_kind=particle_set(i)%atomic_kind, kind_number=ikind, &
     228          133 :                                     element_symbol=element_symbol)
     229          133 :                CALL get_qs_kind(qs_kind_set(ikind), basis_set=orb_basis_set)
     230          277 :                IF (ASSOCIATED(orb_basis_set)) THEN
     231          133 :                   WRITE (iw, '(T2,I8,I8)') i, 0
     232              :                   CALL get_gto_basis_set(gto_basis_set=orb_basis_set, &
     233              :                                          nset=nset, &
     234              :                                          npgf=npgf, &
     235              :                                          nshell=nshell, &
     236              :                                          l=l, &
     237              :                                          zet=zet, &
     238          133 :                                          gcc=gcc)
     239              : 
     240          432 :                   DO iset = 1, nset
     241          765 :                      DO ishell = 1, nshell(iset)
     242          333 :                         lshell = l(ishell, iset)
     243          632 :                         IF (lshell <= molden_lmax) THEN
     244              :                            WRITE (UNIT=iw, FMT='(T25,A2,4X,I4,4X,F4.2)') &
     245          333 :                               angmom(lshell + 1:lshell + 1), npgf(iset), 1.0_dp
     246              :                            ! MOLDEN expects the contraction coefficient of spherical NOT CARTESIAN NORMALISED
     247              :                            ! functions. So we undo the normalisation factors included in the gccs
     248              :                            ! Reverse engineered from basis_set_types, normalise_gcc_orb
     249          333 :                            prefac = 2_dp**lshell*(2/pi)**0.75_dp
     250          333 :                            expzet = 0.25_dp*(2*lshell + 3.0_dp)
     251              :                            WRITE (UNIT=iw, FMT=fmtstr2) &
     252         2156 :                               (zet(ipgf, iset), gcc(ipgf, ishell, iset)/(prefac*zet(ipgf, iset)**expzet), &
     253         2489 :                                ipgf=1, npgf(iset))
     254              :                         ELSE
     255            0 :                            IF (print_warn) THEN
     256              :                               CALL cp_warn(__LOCATION__, &
     257            0 :                                            "MOLDEN format does not support Gaussian orbitals with l > 4.")
     258            0 :                               print_warn = .FALSE.
     259              :                            END IF
     260              :                         END IF
     261              :                      END DO
     262              :                   END DO
     263              : 
     264          133 :                   WRITE (iw, '(A4)') "    "
     265              : 
     266              :                END IF
     267              : 
     268              :             END DO
     269              : 
     270           11 :             IF (gto_kind == gto_spherical) THEN
     271           11 :                WRITE (iw, '(T2,A)') "[5D7F]"
     272           11 :                WRITE (iw, '(T2,A)') "[9G]"
     273              :             END IF
     274              : 
     275           11 :             WRITE (iw, '(T2,A)') "[MO]"
     276              :          END IF
     277              : 
     278              :          !------------------------------------------------------------------------
     279              :          ! convert from CP2K to MOLDEN format ordering
     280              :          ! http://www.cmbi.ru.nl/molden/molden_format.html
     281              :          !"The following order of D, F and G functions is expected:
     282              :          !
     283              :          !   5D: D 0, D+1, D-1, D+2, D-2
     284              :          !   6D: xx, yy, zz, xy, xz, yz
     285              :          !
     286              :          !   7F: F 0, F+1, F-1, F+2, F-2, F+3, F-3
     287              :          !  10F: xxx, yyy, zzz, xyy, xxy, xxz, xzz, yzz, yyz, xyz
     288              :          !
     289              :          !   9G: G 0, G+1, G-1, G+2, G-2, G+3, G-3, G+4, G-4
     290              :          !  15G: xxxx yyyy zzzz xxxy xxxz yyyx yyyz zzzx zzzy,
     291              :          !       xxyy xxzz yyzz xxyz yyxz zzxy
     292              :          !"
     293              :          ! CP2K has x in the outer (slower loop), so
     294              :          ! xx, xy, xz, yy, yz,zz for l=2, for instance
     295              :          !
     296              :          ! iorb_cp2k = orbmap(iorb_molden, l), l = 0 .. 4
     297              :          ! -----------------------------------------------------------------------
     298           22 :          IF (iw > 0) THEN
     299           11 :             IF (gto_kind == gto_cartesian) THEN
     300              :                ! -----------------------------------------------------------------
     301              :                ! Use cartesian (6D, 10F, 15G) representation.
     302              :                ! This is only format VMD can process.
     303              :                ! -----------------------------------------------------------------
     304              :                orbmap = RESHAPE([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &
     305              :                                  1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &
     306              :                                  1, 4, 6, 2, 3, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, &
     307              :                                  1, 7, 10, 4, 2, 3, 6, 9, 8, 5, 0, 0, 0, 0, 0, &
     308              :                                  1, 11, 15, 2, 3, 7, 12, 10, 14, 4, 6, 13, 5, 8, 9], &
     309            0 :                                 [molden_ncomax, molden_lmax + 1])
     310           11 :             ELSE IF (gto_kind == gto_spherical) THEN
     311              :                ! -----------------------------------------------------------------
     312              :                ! Use spherical (5D, 7F, 9G) representation.
     313              :                ! -----------------------------------------------------------------
     314              :                orbmap = RESHAPE([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &
     315              :                                  3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &
     316              :                                  3, 4, 2, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &
     317              :                                  4, 5, 3, 6, 2, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, &
     318              :                                  5, 6, 4, 7, 3, 8, 2, 9, 1, 0, 0, 0, 0, 0, 0], &
     319           11 :                                 [molden_ncomax, molden_lmax + 1])
     320              :             END IF
     321              :          END IF
     322              : 
     323           52 :          DO ispin = 1, SIZE(mos)
     324           30 :             do_calc_energies = .FALSE.
     325           30 :             IF (PRESENT(calc_energies)) do_calc_energies = calc_energies
     326              : 
     327           30 :             IF (PRESENT(qs_env) .AND. do_calc_energies) THEN
     328            4 :                CALL get_qs_env(qs_env, matrix_ks=ks, dft_control=dft_control)
     329              : 
     330            4 :                matrix_ks => ks(ispin)%matrix
     331              : 
     332              :                ! With ADMM, we have to modify the Kohn-Sham matrix
     333            4 :                IF (dft_control%do_admm) THEN
     334            0 :                   CALL get_qs_env(qs_env, admm_env=admm_env)
     335            0 :                   CALL admm_correct_for_eigenvalues(ispin, admm_env, matrix_ks)
     336              :                END IF
     337              : 
     338            4 :                CALL get_mo_set(mo_set=mos(ispin), mo_coeff=mo_coeff, eigenvalues=mo_eigenvalues)
     339              : 
     340            4 :                IF (ASSOCIATED(qs_env%mo_derivs)) THEN
     341            0 :                   mo_coeff_deriv => qs_env%mo_derivs(ispin)%matrix
     342              :                ELSE
     343            4 :                   mo_coeff_deriv => NULL()
     344              :                END IF
     345              : 
     346              :                ! Update the eigenvalues of the occupied orbitals
     347              :                CALL calculate_subspace_eigenvalues(orbitals=mo_coeff, &
     348              :                                                    ks_matrix=matrix_ks, &
     349              :                                                    evals_arg=mo_eigenvalues, &
     350            4 :                                                    co_rotate_dbcsr=mo_coeff_deriv)
     351              : 
     352              :                ! With ADMM, we have to undo the modification of the Kohn-Sham matrix
     353            4 :                IF (dft_control%do_admm) THEN
     354            0 :                   CALL admm_uncorrect_for_eigenvalues(ispin, admm_env, matrix_ks)
     355              :                END IF
     356              :             END IF
     357              : 
     358              :             CALL cp_fm_get_info(mos(ispin)%mo_coeff, &
     359              :                                 nrow_global=nrow_global, &
     360           30 :                                 ncol_global=ncol_global)
     361          120 :             ALLOCATE (smatrix(nrow_global, ncol_global))
     362           30 :             CALL cp_fm_get_submatrix(mos(ispin)%mo_coeff, smatrix)
     363              : 
     364           30 :             IF (iw > 0) THEN
     365           15 :                IF (gto_kind == gto_cartesian) THEN
     366            0 :                   CALL get_qs_kind_set(qs_kind_set, ncgf=ncgf, nsgf=nsgf)
     367              : 
     368            0 :                   ALLOCATE (cmatrix(ncgf, ncgf))
     369              : 
     370            0 :                   cmatrix = 0.0_dp
     371              : 
     372              :                   ! Transform spherical MOs to Cartesian MOs
     373              : 
     374            0 :                   icgf = 1
     375            0 :                   isgf = 1
     376            0 :                   DO iatom = 1, SIZE(particle_set)
     377            0 :                      NULLIFY (orb_basis_set)
     378            0 :                      CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     379              :                      CALL get_qs_kind(qs_kind_set(ikind), &
     380            0 :                                       basis_set=orb_basis_set)
     381            0 :                      IF (ASSOCIATED(orb_basis_set)) THEN
     382              :                         CALL get_gto_basis_set(gto_basis_set=orb_basis_set, &
     383              :                                                nset=nset, &
     384              :                                                nshell=nshell, &
     385            0 :                                                l=l)
     386            0 :                         DO iset = 1, nset
     387            0 :                            DO ishell = 1, nshell(iset)
     388            0 :                               lshell = l(ishell, iset)
     389              :                               CALL dgemm("T", "N", nco(lshell), mos(ispin)%nmo, nso(lshell), 1.0_dp, &
     390              :                                          orbtramat(lshell)%c2s, nso(lshell), &
     391              :                                          smatrix(isgf, 1), nsgf, 0.0_dp, &
     392            0 :                                          cmatrix(icgf, 1), ncgf)
     393            0 :                               icgf = icgf + nco(lshell)
     394            0 :                               isgf = isgf + nso(lshell)
     395              :                            END DO
     396              :                         END DO
     397              :                      END IF
     398              :                   END DO ! iatom
     399              :                END IF
     400              : 
     401           81 :                DO icol = 1, mos(ispin)%nmo
     402              :                   ! index of the first basis function for the given atom, set, and shell
     403           66 :                   irow = 1
     404              : 
     405              :                   ! index of the first basis function in MOLDEN file.
     406              :                   ! Due to limitation of the MOLDEN format, basis functions with l > molden_lmax
     407              :                   ! cannot be exported, so we need to renumber atomic orbitals
     408           66 :                   irow_in = 1
     409              : 
     410           66 :                   WRITE (iw, '(A,ES20.10)') 'Ene=', mos(ispin)%eigenvalues(icol)
     411           66 :                   IF (ispin < 2) THEN
     412           61 :                      WRITE (iw, '(A)') 'Spin= Alpha'
     413              :                   ELSE
     414            5 :                      WRITE (iw, '(A)') 'Spin= Beta'
     415              :                   END IF
     416           66 :                   WRITE (iw, '(A,F12.7)') 'Occup=', mos(ispin)%occupation_numbers(icol)
     417              : 
     418          539 :                   DO iatom = 1, SIZE(particle_set)
     419          458 :                      NULLIFY (orb_basis_set)
     420              :                      CALL get_atomic_kind(particle_set(iatom)%atomic_kind, &
     421          458 :                                           element_symbol=element_symbol, kind_number=ikind)
     422              :                      CALL get_qs_kind(qs_kind_set(ikind), &
     423          458 :                                       basis_set=orb_basis_set)
     424          982 :                      IF (ASSOCIATED(orb_basis_set)) THEN
     425              :                         CALL get_gto_basis_set(gto_basis_set=orb_basis_set, &
     426              :                                                nset=nset, &
     427              :                                                nshell=nshell, &
     428          458 :                                                l=l)
     429              : 
     430          458 :                         IF (gto_kind == gto_cartesian) THEN
     431              :                            ! ----------------------------------------------
     432              :                            ! Use cartesian (6D, 10F, 15G) representation.
     433              :                            ! ----------------------------------------------
     434            0 :                            icgf = 1
     435            0 :                            DO iset = 1, nset
     436            0 :                               DO ishell = 1, nshell(iset)
     437            0 :                                  lshell = l(ishell, iset)
     438              : 
     439            0 :                                  IF (lshell <= molden_lmax) THEN
     440              :                                     CALL print_coeffs(iw, fmtstr1, ndigits, irow_in, orbmap(:, lshell), &
     441            0 :                                                       cmatrix(irow:irow + nco(lshell) - 1, icol))
     442            0 :                                     irow_in = irow_in + nco(lshell)
     443              :                                  END IF
     444              : 
     445            0 :                                  irow = irow + nco(lshell)
     446              :                               END DO ! ishell
     447              :                            END DO
     448              : 
     449          458 :                         ELSE IF (gto_kind == gto_spherical) THEN
     450              :                            ! ----------------------------------------------
     451              :                            ! Use spherical (5D, 7F, 9G) representation.
     452              :                            ! ----------------------------------------------
     453         1704 :                            DO iset = 1, nset
     454         3130 :                               DO ishell = 1, nshell(iset)
     455         1426 :                                  lshell = l(ishell, iset)
     456              : 
     457         1426 :                                  IF (lshell <= molden_lmax) THEN
     458              :                                     CALL print_coeffs(iw, fmtstr1, ndigits, irow_in, orbmap(:, lshell), &
     459         1426 :                                                       smatrix(irow:irow + nso(lshell) - 1, icol))
     460         1426 :                                     irow_in = irow_in + nso(lshell)
     461              :                                  END IF
     462              : 
     463         2672 :                                  irow = irow + nso(lshell)
     464              :                               END DO
     465              :                            END DO
     466              :                         END IF
     467              : 
     468              :                      END IF
     469              :                   END DO ! iatom
     470              :                END DO
     471              :             END IF
     472              : 
     473           30 :             IF (ALLOCATED(cmatrix)) DEALLOCATE (cmatrix)
     474           82 :             IF (ALLOCATED(smatrix)) DEALLOCATE (smatrix)
     475              :          END DO
     476              : 
     477              :          ! Write unoccupied (virtual) orbitals if provided; only used with OT
     478           22 :          IF (PRESENT(unoccupied_orbs) .AND. PRESENT(unoccupied_evals)) THEN
     479            0 :             DO ispin = 1, SIZE(unoccupied_orbs)
     480              :                CALL cp_fm_get_info(unoccupied_orbs(ispin), &
     481              :                                    nrow_global=nrow_global, &
     482            0 :                                    ncol_global=numos)
     483            0 :                ALLOCATE (smatrix(nrow_global, numos))
     484            0 :                CALL cp_fm_get_submatrix(unoccupied_orbs(ispin), smatrix)
     485              : 
     486            0 :                IF (iw > 0) THEN
     487            0 :                   IF (gto_kind == gto_cartesian) THEN
     488            0 :                      CALL get_qs_kind_set(qs_kind_set, ncgf=ncgf, nsgf=nsgf)
     489            0 :                      ALLOCATE (cmatrix(ncgf, numos))
     490            0 :                      cmatrix = 0.0_dp
     491              : 
     492            0 :                      icgf = 1
     493            0 :                      isgf = 1
     494            0 :                      DO iatom = 1, SIZE(particle_set)
     495            0 :                         NULLIFY (orb_basis_set)
     496            0 :                         CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     497            0 :                         CALL get_qs_kind(qs_kind_set(ikind), basis_set=orb_basis_set)
     498            0 :                         IF (ASSOCIATED(orb_basis_set)) THEN
     499              :                            CALL get_gto_basis_set(gto_basis_set=orb_basis_set, &
     500            0 :                                                   nset=nset, nshell=nshell, l=l)
     501            0 :                            DO iset = 1, nset
     502            0 :                               DO ishell = 1, nshell(iset)
     503            0 :                                  lshell = l(ishell, iset)
     504              :                                  CALL dgemm("T", "N", nco(lshell), numos, nso(lshell), 1.0_dp, &
     505              :                                             orbtramat(lshell)%c2s, nso(lshell), &
     506              :                                             smatrix(isgf, 1), nsgf, 0.0_dp, &
     507            0 :                                             cmatrix(icgf, 1), ncgf)
     508            0 :                                  icgf = icgf + nco(lshell)
     509            0 :                                  isgf = isgf + nso(lshell)
     510              :                               END DO
     511              :                            END DO
     512              :                         END IF
     513              :                      END DO
     514              :                   END IF
     515              : 
     516            0 :                   DO icol = 1, numos
     517            0 :                      irow = 1
     518            0 :                      irow_in = 1
     519              : 
     520            0 :                      WRITE (iw, '(A,ES20.10)') 'Ene=', unoccupied_evals(ispin)%array(icol)
     521            0 :                      IF (ispin < 2) THEN
     522            0 :                         WRITE (iw, '(A)') 'Spin= Alpha'
     523              :                      ELSE
     524            0 :                         WRITE (iw, '(A)') 'Spin= Beta'
     525              :                      END IF
     526            0 :                      WRITE (iw, '(A,F12.7)') 'Occup=', 0.0_dp
     527              : 
     528            0 :                      DO iatom = 1, SIZE(particle_set)
     529            0 :                         NULLIFY (orb_basis_set)
     530              :                         CALL get_atomic_kind(particle_set(iatom)%atomic_kind, &
     531            0 :                                              element_symbol=element_symbol, kind_number=ikind)
     532            0 :                         CALL get_qs_kind(qs_kind_set(ikind), basis_set=orb_basis_set)
     533            0 :                         IF (ASSOCIATED(orb_basis_set)) THEN
     534              :                            CALL get_gto_basis_set(gto_basis_set=orb_basis_set, &
     535            0 :                                                   nset=nset, nshell=nshell, l=l)
     536              : 
     537            0 :                            IF (gto_kind == gto_cartesian) THEN
     538            0 :                               icgf = 1
     539            0 :                               DO iset = 1, nset
     540            0 :                                  DO ishell = 1, nshell(iset)
     541            0 :                                     lshell = l(ishell, iset)
     542            0 :                                     IF (lshell <= molden_lmax) THEN
     543              :                                        CALL print_coeffs(iw, fmtstr1, ndigits, irow_in, orbmap(:, lshell), &
     544            0 :                                                          cmatrix(irow:irow + nco(lshell) - 1, icol))
     545            0 :                                        irow_in = irow_in + nco(lshell)
     546              :                                     END IF
     547            0 :                                     irow = irow + nco(lshell)
     548              :                                  END DO
     549              :                               END DO
     550            0 :                            ELSE IF (gto_kind == gto_spherical) THEN
     551            0 :                               DO iset = 1, nset
     552            0 :                                  DO ishell = 1, nshell(iset)
     553            0 :                                     lshell = l(ishell, iset)
     554            0 :                                     IF (lshell <= molden_lmax) THEN
     555              :                                        CALL print_coeffs(iw, fmtstr1, ndigits, irow_in, orbmap(:, lshell), &
     556            0 :                                                          smatrix(irow:irow + nso(lshell) - 1, icol))
     557            0 :                                        irow_in = irow_in + nso(lshell)
     558              :                                     END IF
     559            0 :                                     irow = irow + nso(lshell)
     560              :                                  END DO
     561              :                               END DO
     562              :                            END IF
     563              : 
     564              :                         END IF
     565              :                      END DO ! iatom
     566              :                   END DO ! icol
     567              :                END IF
     568              : 
     569            0 :                IF (ALLOCATED(cmatrix)) DEALLOCATE (cmatrix)
     570            0 :                IF (ALLOCATED(smatrix)) DEALLOCATE (smatrix)
     571              :             END DO ! ispin
     572              :          END IF
     573              : 
     574           22 :          CALL cp_print_key_finished_output(iw, logger, print_section, "")
     575              : 
     576              :       END IF
     577              : 
     578        12021 :       CALL timestop(handle)
     579              : 
     580        24042 :    END SUBROUTINE write_mos_molden
     581              : 
     582              : ! **************************************************************************************************
     583              : !> \brief Output MO coefficients formatted correctly for MOLDEN, omitting those <= 1E(-digits)
     584              : !> \param iw       output file unit
     585              : !> \param fmtstr1  format string
     586              : !> \param ndigits  number of significant digits in MO coefficients
     587              : !> \param irow_in  index of the first atomic orbital: mo_coeff(orbmap(1))
     588              : !> \param orbmap   array to map Gaussian functions from MOLDEN to CP2K ordering
     589              : !> \param mo_coeff MO coefficients
     590              : ! **************************************************************************************************
     591         1426 :    SUBROUTINE print_coeffs(iw, fmtstr1, ndigits, irow_in, orbmap, mo_coeff)
     592              :       INTEGER, INTENT(in)                                :: iw
     593              :       CHARACTER(LEN=*), INTENT(in)                       :: fmtstr1
     594              :       INTEGER, INTENT(in)                                :: ndigits, irow_in
     595              :       INTEGER, DIMENSION(molden_ncomax), INTENT(in)      :: orbmap
     596              :       REAL(KIND=dp), DIMENSION(:), INTENT(in)            :: mo_coeff
     597              : 
     598              :       INTEGER                                            :: orbital
     599              : 
     600        22816 :       DO orbital = 1, molden_ncomax
     601        22816 :          IF (orbmap(orbital) /= 0) THEN
     602         2686 :             IF (ABS(mo_coeff(orbmap(orbital))) >= 10.0_dp**(-ndigits)) THEN
     603         1692 :                WRITE (iw, fmtstr1) irow_in + orbital - 1, mo_coeff(orbmap(orbital))
     604              :             END IF
     605              :          END IF
     606              :       END DO
     607              : 
     608         1426 :    END SUBROUTINE print_coeffs
     609              : 
     610              : ! **************************************************************************************************
     611              : !> \brief writes the output for vibrational analysis in MOLDEN format
     612              : !> \param input ...
     613              : !> \param particles ...
     614              : !> \param freq ...
     615              : !> \param eigen_vec ...
     616              : !> \param intensities ...
     617              : !> \param calc_intens ...
     618              : !> \param dump_only_positive ...
     619              : !> \param logger ...
     620              : !> \param list array of mobile atom indices
     621              : !> \param cell optional simulation cell for the CP2K [Cell] extension
     622              : !> \author Florian Schiffmann 11.2007
     623              : ! **************************************************************************************************
     624           60 :    SUBROUTINE write_vibrations_molden(input, particles, freq, eigen_vec, intensities, calc_intens, &
     625              :                                       dump_only_positive, logger, list, cell)
     626              : 
     627              :       TYPE(section_vals_type), POINTER                   :: input
     628              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particles
     629              :       REAL(KIND=dp), DIMENSION(:)                        :: freq
     630              :       REAL(KIND=dp), DIMENSION(:, :)                     :: eigen_vec
     631              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: intensities
     632              :       LOGICAL, INTENT(in)                                :: calc_intens, dump_only_positive
     633              :       TYPE(cp_logger_type), POINTER                      :: logger
     634              :       INTEGER, DIMENSION(:), OPTIONAL, POINTER           :: list
     635              :       TYPE(cell_type), OPTIONAL, POINTER                 :: cell
     636              : 
     637              :       CHARACTER(len=*), PARAMETER :: routineN = 'write_vibrations_molden'
     638              : 
     639              :       CHARACTER(LEN=2)                                   :: element_symbol
     640              :       INTEGER                                            :: handle, i, iw, j, k, l, z
     641           60 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: my_list
     642              :       LOGICAL                                            :: write_cell
     643              :       REAL(KIND=dp)                                      :: fint
     644              : 
     645           60 :       CALL timeset(routineN, handle)
     646              : 
     647              :       iw = cp_print_key_unit_nr(logger, input, "VIBRATIONAL_ANALYSIS%PRINT%MOLDEN_VIB", &
     648           60 :                                 extension=".mol", file_status='REPLACE')
     649              : 
     650           60 :       IF (iw > 0) THEN
     651           30 :          CPASSERT(MOD(SIZE(eigen_vec, 1), 3) == 0)
     652           30 :          CPASSERT(SIZE(freq, 1) == SIZE(eigen_vec, 2))
     653           90 :          ALLOCATE (my_list(SIZE(particles)))
     654              :          ! Either we have a list of the subset of mobile atoms,
     655              :          ! Or the eigenvectors must span the full space (all atoms)
     656           30 :          IF (PRESENT(list)) THEN
     657           16 :             my_list(:) = 0
     658           60 :             DO i = 1, SIZE(list)
     659           60 :                my_list(list(i)) = i
     660              :             END DO
     661              :          ELSE
     662           14 :             CPASSERT(SIZE(particles) == SIZE(eigen_vec, 1)/3)
     663          534 :             DO i = 1, SIZE(my_list)
     664          534 :                my_list(i) = i
     665              :             END DO
     666              :          END IF
     667           30 :          WRITE (iw, '(T2,A)') "[Molden Format]"
     668              :          CALL section_vals_val_get(input, "VIBRATIONAL_ANALYSIS%PRINT%MOLDEN_VIB%WRITE_CELL", &
     669           30 :                                    l_val=write_cell)
     670           30 :          IF (write_cell) THEN
     671            0 :             CPASSERT(PRESENT(cell))
     672            0 :             CPASSERT(ASSOCIATED(cell))
     673            0 :             CALL write_cell_molden(iw, cell, 1)
     674              :          END IF
     675           30 :          WRITE (iw, '(T2,A)') "[Atoms] AU"
     676          597 :          DO i = 1, SIZE(particles)
     677              :             CALL get_atomic_kind(atomic_kind=particles(i)%atomic_kind, &
     678          567 :                                  element_symbol=element_symbol)
     679          567 :             CALL get_ptable_info(element_symbol, number=z)
     680              : 
     681              :             WRITE (iw, '(T2,A2,I8,I8,3X,3(F12.6,3X))') &
     682         2298 :                element_symbol, i, z, particles(i)%r(:)
     683              : 
     684              :          END DO
     685           30 :          WRITE (iw, '(T2,A)') "[FREQ]"
     686          183 :          DO i = 1, SIZE(freq, 1)
     687          183 :             IF ((.NOT. dump_only_positive) .OR. (freq(i) >= 0._dp)) WRITE (iw, '(T5,F12.6)') freq(i)
     688              :          END DO
     689           30 :          WRITE (iw, '(T2,A)') "[FR-COORD]"
     690          597 :          DO i = 1, SIZE(particles)
     691              :             CALL get_atomic_kind(atomic_kind=particles(i)%atomic_kind, &
     692          567 :                                  element_symbol=element_symbol)
     693              :             WRITE (iw, '(T2,A2,3X,3(F12.6,3X))') &
     694         2298 :                element_symbol, particles(i)%r(:)
     695              :          END DO
     696           30 :          WRITE (iw, '(T2,A)') "[FR-NORM-COORD]"
     697           30 :          l = 0
     698          183 :          DO i = 1, SIZE(eigen_vec, 2)
     699          183 :             IF ((.NOT. dump_only_positive) .OR. (freq(i) >= 0._dp)) THEN
     700          153 :                l = l + 1
     701          153 :                WRITE (iw, '(T2,A,1X,I6)') "vibration", l
     702         4758 :                DO j = 1, SIZE(particles)
     703         4758 :                   IF (my_list(j) /= 0) THEN
     704         4593 :                      k = (my_list(j) - 1)*3
     705         4593 :                      WRITE (iw, '(T2,3(F12.6,3X))') eigen_vec(k + 1, i), eigen_vec(k + 2, i), eigen_vec(k + 3, i)
     706              :                   ELSE
     707           12 :                      WRITE (iw, '(T2,3(F12.6,3X))') 0.0_dp, 0.0_dp, 0.0_dp
     708              :                   END IF
     709              :                END DO
     710              :             END IF
     711              :          END DO
     712           30 :          IF (calc_intens) THEN
     713           19 :             fint = massunit
     714              :             ! intensity units are a.u./amu
     715           19 :             WRITE (iw, '(T2,A)') "[INT]"
     716          128 :             DO i = 1, SIZE(intensities)
     717          128 :                IF ((.NOT. dump_only_positive) .OR. (freq(i) >= 0._dp)) WRITE (iw, '(3X,F18.6)') fint*intensities(i)**2
     718              :             END DO
     719              :          END IF
     720           60 :          DEALLOCATE (my_list)
     721              :       END IF
     722           60 :       CALL cp_print_key_finished_output(iw, logger, input, "VIBRATIONAL_ANALYSIS%PRINT%MOLDEN_VIB")
     723              : 
     724           60 :       CALL timestop(handle)
     725              : 
     726           60 :    END SUBROUTINE write_vibrations_molden
     727              : 
     728              : END MODULE molden_utils
        

Generated by: LCOV version 2.0-1