LCOV - code coverage report
Current view: top level - src - trexio_utils.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:2fce0f8) Lines: 465 480 96.9 %
Date: 2024-12-21 06:28:57 Functions: 4 4 100.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief The module to read/write TREX IO files for interfacing CP2K with other programs
      10             : !> \par History
      11             : !>      05.2024 created [SB]
      12             : !> \author Stefano Battaglia
      13             : ! **************************************************************************************************
      14             : MODULE trexio_utils
      15             : 
      16             :    USE atomic_kind_types, ONLY: get_atomic_kind
      17             :    USE basis_set_types, ONLY: gto_basis_set_type, get_gto_basis_set
      18             :    USE cell_types, ONLY: cell_type
      19             :    USE cp2k_info, ONLY: cp2k_version
      20             :    USE cp_blacs_env, ONLY: cp_blacs_env_type
      21             :    USE cp_control_types, ONLY: dft_control_type
      22             :    USE cp_dbcsr_operations, ONLY: copy_dbcsr_to_fm
      23             :    USE cp_files, ONLY: close_file, file_exists, open_file
      24             :    USE cp_fm_types, ONLY: cp_fm_get_info, cp_fm_type, cp_fm_create, cp_fm_set_all, &
      25             :                           cp_fm_get_submatrix, cp_fm_to_fm_submat_general, cp_fm_release
      26             :    USE cp_fm_struct, ONLY: cp_fm_struct_create, &
      27             :                            cp_fm_struct_release, &
      28             :                            cp_fm_struct_type
      29             :    USE cp_log_handling, ONLY: cp_get_default_logger, &
      30             :                               cp_logger_get_default_io_unit, &
      31             :                               cp_logger_type
      32             :    USE external_potential_types, ONLY: sgp_potential_type, get_potential
      33             :    USE input_section_types, ONLY: section_vals_type, section_vals_get, &
      34             :                                   section_vals_val_get
      35             :    USE kinds, ONLY: default_path_length, dp
      36             :    USE kpoint_types, ONLY: kpoint_type, kpoint_env_p_type, &
      37             :                            get_kpoint_info, get_kpoint_env
      38             :    USE mathconstants, ONLY: fourpi, pi
      39             :    USE message_passing, ONLY: mp_para_env_type
      40             :    USE orbital_pointers, ONLY: nco, nso
      41             :    USE orbital_transformation_matrices, ONLY: orbtramat
      42             :    USE particle_types, ONLY: particle_type
      43             :    USE qs_energy_types, ONLY: qs_energy_type
      44             :    USE qs_environment_types, ONLY: get_qs_env, &
      45             :                                    qs_environment_type
      46             :    USE qs_kind_types, ONLY: get_qs_kind, get_qs_kind_set, &
      47             :                             qs_kind_type
      48             :    USE qs_mo_types, ONLY: mo_set_type, get_mo_set
      49             : #ifdef __TREXIO
      50             :    USE trexio, ONLY: trexio_open, trexio_close, &
      51             :                      TREXIO_HDF5, TREXIO_SUCCESS, &
      52             :                      trexio_string_of_error, trexio_t, trexio_exit_code, &
      53             :                      trexio_write_metadata_code, trexio_write_metadata_code_num, &
      54             :                      trexio_write_nucleus_coord, trexio_write_nucleus_num, &
      55             :                      trexio_write_nucleus_charge, trexio_write_nucleus_label, &
      56             :                      trexio_write_nucleus_repulsion, &
      57             :                      trexio_write_cell_a, trexio_write_cell_b, trexio_write_cell_c, &
      58             :                      trexio_write_cell_g_a, trexio_write_cell_g_b, &
      59             :                      trexio_write_cell_g_c, trexio_write_cell_two_pi, &
      60             :                      trexio_write_pbc_periodic, trexio_write_pbc_k_point_num, &
      61             :                      trexio_write_pbc_k_point, trexio_write_pbc_k_point_weight, &
      62             :                      trexio_write_electron_num, trexio_write_electron_up_num, &
      63             :                      trexio_write_electron_dn_num, &
      64             :                      trexio_write_state_num, trexio_write_state_id, &
      65             :                      trexio_write_state_energy, &
      66             :                      trexio_write_basis_type, trexio_write_basis_prim_num, &
      67             :                      trexio_write_basis_shell_num, trexio_write_basis_nucleus_index, &
      68             :                      trexio_write_basis_shell_ang_mom, trexio_write_basis_shell_factor, &
      69             :                      trexio_write_basis_r_power, trexio_write_basis_shell_index, &
      70             :                      trexio_write_basis_exponent, trexio_write_basis_coefficient, &
      71             :                      trexio_write_basis_prim_factor, &
      72             :                      trexio_write_ecp_z_core, trexio_write_ecp_max_ang_mom_plus_1, &
      73             :                      trexio_write_ecp_num, trexio_write_ecp_ang_mom, &
      74             :                      trexio_write_ecp_nucleus_index, trexio_write_ecp_exponent, &
      75             :                      trexio_write_ecp_coefficient, trexio_write_ecp_power, &
      76             :                      trexio_write_ao_cartesian, trexio_write_ao_num, &
      77             :                      trexio_write_ao_shell, trexio_write_ao_normalization, &
      78             :                      trexio_write_mo_num, trexio_write_mo_energy, &
      79             :                      trexio_write_mo_occupation, trexio_write_mo_spin, &
      80             :                      trexio_write_mo_class, trexio_write_mo_coefficient, &
      81             :                      trexio_write_mo_coefficient_im, trexio_write_mo_k_point, &
      82             :                      trexio_write_mo_type
      83             : #endif
      84             : #include "./base/base_uses.f90"
      85             : 
      86             :    IMPLICIT NONE
      87             : 
      88             :    PRIVATE
      89             : 
      90             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'trexio_utils'
      91             : 
      92             :    PUBLIC :: write_trexio
      93             : 
      94             : CONTAINS
      95             : 
      96             : ! **************************************************************************************************
      97             : !> \brief Write a trexio file
      98             : !> \param qs_env the qs environment with all the info of the computation
      99             : !> \param trexio_section the section with the trexio info
     100             : ! **************************************************************************************************
     101           8 :    SUBROUTINE write_trexio(qs_env, trexio_section)
     102             :       TYPE(qs_environment_type), INTENT(IN), POINTER     :: qs_env
     103             :       TYPE(section_vals_type), INTENT(IN), POINTER       :: trexio_section
     104             : 
     105             :       CHARACTER(LEN=*), PARAMETER :: routineN = 'write_trexio'
     106             : 
     107             :       INTEGER                                            :: handle
     108             : 
     109             : #ifdef __TREXIO
     110             :       INTEGER                                            :: output_unit, unit_trexio
     111             :       CHARACTER(len=default_path_length)                 :: filename
     112             :       INTEGER(trexio_t)                                  :: f        ! The TREXIO file handle
     113             :       INTEGER(trexio_exit_code)                          :: rc       ! TREXIO return code
     114             :       LOGICAL                                            :: explicit, do_kpoints, ecp_semi_local, &
     115             :                                                             ecp_local, sgp_potential_present, ionode, &
     116             :                                                             use_real_wfn, save_cartesian
     117             :       REAL(KIND=dp)                                      :: e_nn, zeff, expzet, prefac, zeta, gcca
     118             :       TYPE(cell_type), POINTER                           :: cell => Null()
     119             :       TYPE(cp_logger_type), POINTER                      :: logger => Null()
     120             :       TYPE(dft_control_type), POINTER                    :: dft_control => Null()
     121             :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
     122             :       TYPE(kpoint_type), POINTER                         :: kpoints => Null()
     123             :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set => Null()
     124             :       TYPE(qs_energy_type), POINTER                      :: energy => Null()
     125             :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: kind_set => Null()
     126             :       TYPE(sgp_potential_type), POINTER                  :: sgp_potential => Null()
     127             :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos => Null()
     128             :       TYPE(mo_set_type), DIMENSION(:, :), POINTER        :: mos_kp => Null()
     129             :       TYPE(kpoint_env_p_type), DIMENSION(:), POINTER     :: kp_env => Null()
     130             :       TYPE(mp_para_env_type), POINTER                    :: para_env => Null(), para_env_inter_kp => Null()
     131             :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env => Null()
     132             :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct => Null()
     133             :       TYPE(cp_fm_type)                                   :: fm_mo_coeff, fm_dummy, fm_mo_coeff_im
     134             : 
     135             :       CHARACTER(LEN=2)                                   :: element_symbol
     136           8 :       CHARACTER(LEN=2), DIMENSION(:), ALLOCATABLE        :: label
     137             :       INTEGER                                            :: iatom, natoms, periodic, nkp, nel_tot, &
     138             :                                                             nspins, ikind, ishell_loc, ishell, &
     139             :                                                             shell_num, prim_num, nset, iset, ipgf, z, &
     140             :                                                             sl_lmax, ecp_num, nloc, nsemiloc, sl_l, iecp, &
     141             :                                                             igf, icgf, ncgf, ngf_shell, lshell, ao_num, nmo, &
     142             :                                                             mo_num, ispin, ikp, imo, ikp_loc, nsgf, &
     143             :                                                             i, k, l, m
     144             :       INTEGER, DIMENSION(2)                              :: nel_spin, kp_range, nmo_spin
     145             :       INTEGER, DIMENSION(3)                              :: nkp_grid
     146             :       INTEGER, DIMENSION(0:10)                           :: npot
     147           8 :       INTEGER, DIMENSION(:), ALLOCATABLE                 :: nucleus_index, shell_ang_mom, r_power, &
     148           8 :                                                             shell_index, z_core, max_ang_mom_plus_1, &
     149           8 :                                                             ang_mom, powers, ao_shell, mo_spin, mo_kpoint
     150             :       INTEGER, DIMENSION(:), POINTER                     :: nshell => Null(), npgf => Null()
     151             :       INTEGER, DIMENSION(:, :), POINTER                  :: l_shell_set => Null()
     152           8 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE           :: charge, shell_factor, exponents, coefficients, &
     153           8 :                                                             prim_factor, ao_normalization, mo_energy, &
     154           8 :                                                             mo_occupation
     155             :       REAL(KIND=dp), DIMENSION(:), POINTER               :: wkp => Null(), norm_cgf => Null()
     156           8 :       REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE        :: coord, mo_coefficient, mo_coefficient_im, &
     157           8 :                                                             mos_sgf, diag_nsgf, diag_ncgf, temp
     158             :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: zetas => Null()
     159             :       REAL(KIND=dp), DIMENSION(:, :, :), POINTER         :: gcc => Null()
     160             : #endif
     161             : 
     162           8 :       CALL timeset(routineN, handle)
     163             : 
     164             : #ifdef __TREXIO
     165           8 :       logger => cp_get_default_logger()
     166           8 :       output_unit = cp_logger_get_default_io_unit(logger)
     167             : 
     168           8 :       CPASSERT(ASSOCIATED(qs_env))
     169             : 
     170             :       ! get filename
     171           8 :       CALL section_vals_val_get(trexio_section, "FILENAME", c_val=filename, explicit=explicit)
     172           8 :       IF (.NOT. explicit) THEN
     173           6 :          filename = TRIM(logger%iter_info%project_name)//'-TREXIO.h5'
     174             :       ELSE
     175           2 :          filename = TRIM(filename)//'.h5'
     176             :       END IF
     177             : 
     178           8 :       CALL get_qs_env(qs_env, para_env=para_env)
     179           8 :       ionode = para_env%is_source()
     180             : 
     181             :       ! inquire whether a file with the same name already exists, if yes, delete it
     182           8 :       IF (ionode) THEN
     183           4 :          IF (file_exists(filename)) THEN
     184           0 :             CALL open_file(filename, unit_number=unit_trexio)
     185           0 :             CALL close_file(unit_number=unit_trexio, file_status="DELETE")
     186             :          END IF
     187             : 
     188             :          !========================================================================================!
     189             :          ! Open the TREXIO file
     190             :          !========================================================================================!
     191           4 :          f = trexio_open(filename, 'w', TREXIO_HDF5, rc)
     192           4 :          CALL trexio_error(rc)
     193             : 
     194             :          !========================================================================================!
     195             :          ! Metadata group
     196             :          !========================================================================================!
     197           4 :          rc = trexio_write_metadata_code_num(f, 1)
     198           4 :          CALL trexio_error(rc)
     199             : 
     200           4 :          rc = trexio_write_metadata_code(f, cp2k_version, LEN_TRIM(cp2k_version) + 1)
     201           4 :          CALL trexio_error(rc)
     202             : 
     203             :          !========================================================================================!
     204             :          ! Nucleus group
     205             :          !========================================================================================!
     206           4 :          CALL get_qs_env(qs_env, cell=cell, particle_set=particle_set, qs_kind_set=kind_set, natom=natoms)
     207             : 
     208           4 :          rc = trexio_write_nucleus_num(f, natoms)
     209           4 :          CALL trexio_error(rc)
     210             : 
     211          12 :          ALLOCATE (coord(3, natoms))
     212           8 :          ALLOCATE (label(natoms))
     213          12 :          ALLOCATE (charge(natoms))
     214          17 :          DO iatom = 1, natoms
     215             :             ! store the coordinates
     216          52 :             coord(:, iatom) = particle_set(iatom)%r(1:3)
     217             :             ! figure out the element symbol and to which kind_set entry this atomic_kind corresponds to
     218          13 :             CALL get_atomic_kind(particle_set(iatom)%atomic_kind, element_symbol=element_symbol, kind_number=ikind)
     219             :             ! store the element symbol
     220          13 :             label(iatom) = element_symbol
     221             :             ! get and store the effective nuclear charge of this kind_type (ikind)
     222          13 :             CALL get_qs_kind(kind_set(ikind), zeff=zeff)
     223          17 :             charge(iatom) = zeff
     224             :          END DO
     225             : 
     226           4 :          rc = trexio_write_nucleus_coord(f, coord)
     227           4 :          CALL trexio_error(rc)
     228           4 :          DEALLOCATE (coord)
     229             : 
     230           4 :          rc = trexio_write_nucleus_charge(f, charge)
     231           4 :          CALL trexio_error(rc)
     232           4 :          DEALLOCATE (charge)
     233             : 
     234           4 :          rc = trexio_write_nucleus_label(f, label, 3)
     235           4 :          CALL trexio_error(rc)
     236           4 :          DEALLOCATE (label)
     237             : 
     238             :          ! nuclear repulsion energy well-defined for molecules only
     239          16 :          IF (SUM(cell%perd) == 0) THEN
     240           2 :             CALL nuclear_repulsion_energy(particle_set, kind_set, e_nn)
     241           2 :             rc = trexio_write_nucleus_repulsion(f, e_nn)
     242           2 :             CALL trexio_error(rc)
     243             :          END IF
     244             : 
     245             :          !========================================================================================!
     246             :          ! Cell group
     247             :          !========================================================================================!
     248           4 :          rc = trexio_write_cell_a(f, cell%hmat(:, 1))
     249           4 :          CALL trexio_error(rc)
     250             : 
     251           4 :          rc = trexio_write_cell_b(f, cell%hmat(:, 2))
     252           4 :          CALL trexio_error(rc)
     253             : 
     254           4 :          rc = trexio_write_cell_c(f, cell%hmat(:, 3))
     255           4 :          CALL trexio_error(rc)
     256             : 
     257           4 :          rc = trexio_write_cell_g_a(f, cell%h_inv(:, 1))
     258           4 :          CALL trexio_error(rc)
     259             : 
     260           4 :          rc = trexio_write_cell_g_b(f, cell%h_inv(:, 2))
     261           4 :          CALL trexio_error(rc)
     262             : 
     263           4 :          rc = trexio_write_cell_g_c(f, cell%h_inv(:, 3))
     264           4 :          CALL trexio_error(rc)
     265             : 
     266           4 :          rc = trexio_write_cell_two_pi(f, 0)
     267           4 :          CALL trexio_error(rc)
     268             : 
     269             :          !========================================================================================!
     270             :          ! PBC group
     271             :          !========================================================================================!
     272           4 :          CALL get_qs_env(qs_env, do_kpoints=do_kpoints, kpoints=kpoints)
     273             : 
     274           4 :          periodic = 0
     275          16 :          IF (SUM(cell%perd) /= 0) periodic = 1
     276           4 :          rc = trexio_write_pbc_periodic(f, periodic)
     277           4 :          CALL trexio_error(rc)
     278             : 
     279           4 :          IF (do_kpoints) THEN
     280           1 :             CALL get_kpoint_info(kpoints, nkp=nkp, nkp_grid=nkp_grid, wkp=wkp)
     281             : 
     282           1 :             rc = trexio_write_pbc_k_point_num(f, nkp)
     283           1 :             CALL trexio_error(rc)
     284             : 
     285           4 :             rc = trexio_write_pbc_k_point(f, REAL(nkp_grid, KIND=dp))
     286           1 :             CALL trexio_error(rc)
     287             : 
     288           1 :             rc = trexio_write_pbc_k_point_weight(f, wkp)
     289           1 :             CALL trexio_error(rc)
     290             :          END IF
     291             : 
     292             :          !========================================================================================!
     293             :          ! Electron group
     294             :          !========================================================================================!
     295           4 :          CALL get_qs_env(qs_env, dft_control=dft_control, nelectron_total=nel_tot)
     296             : 
     297           4 :          rc = trexio_write_electron_num(f, nel_tot)
     298           4 :          CALL trexio_error(rc)
     299             : 
     300           4 :          nspins = dft_control%nspins
     301           4 :          IF (nspins == 1) THEN
     302             :             ! it is a spin-restricted calculation and we need to split the electrons manually,
     303             :             ! because in CP2K they are all otherwise weirdly stored in nelectron_spin(1)
     304           3 :             nel_spin(1) = nel_tot/2
     305           3 :             nel_spin(2) = nel_tot/2
     306             :          ELSE
     307             :             ! for UKS/ROKS, the two spin channels are populated correctly and according to
     308             :             ! the multiplicity
     309           1 :             CALL get_qs_env(qs_env, nelectron_spin=nel_spin)
     310             :          END IF
     311           4 :          rc = trexio_write_electron_up_num(f, nel_spin(1))
     312           4 :          CALL trexio_error(rc)
     313           4 :          rc = trexio_write_electron_dn_num(f, nel_spin(2))
     314           4 :          CALL trexio_error(rc)
     315             : 
     316             :          !========================================================================================!
     317             :          ! State group
     318             :          !========================================================================================!
     319           4 :          CALL get_qs_env(qs_env, energy=energy)
     320             : 
     321           4 :          rc = trexio_write_state_num(f, 1)
     322           4 :          CALL trexio_error(rc)
     323             : 
     324           4 :          rc = trexio_write_state_id(f, 1)
     325           4 :          CALL trexio_error(rc)
     326             : 
     327           4 :          rc = trexio_write_state_energy(f, energy%total)
     328           4 :          CALL trexio_error(rc)
     329             : 
     330             :       END IF ! ionode
     331             : 
     332             :       !========================================================================================!
     333             :       ! Basis group
     334             :       !========================================================================================!
     335           8 :       CALL get_qs_env(qs_env, qs_kind_set=kind_set, natom=natoms, particle_set=particle_set)
     336           8 :       CALL get_qs_kind_set(kind_set, nshell=shell_num, npgf_seg=prim_num)
     337             : 
     338           8 :       IF (ionode) THEN
     339           4 :          rc = trexio_write_basis_type(f, 'Gaussian', LEN_TRIM('Gaussian') + 1)
     340           4 :          CALL trexio_error(rc)
     341             : 
     342           4 :          rc = trexio_write_basis_shell_num(f, shell_num)
     343           4 :          CALL trexio_error(rc)
     344             : 
     345           4 :          rc = trexio_write_basis_prim_num(f, prim_num)
     346           4 :          CALL trexio_error(rc)
     347             :       END IF ! ionode
     348             : 
     349             :       ! one-to-one mapping between shells and ...
     350          24 :       ALLOCATE (nucleus_index(shell_num)) ! ...atomic indices
     351          16 :       ALLOCATE (shell_ang_mom(shell_num)) ! ...angular momenta
     352          24 :       ALLOCATE (shell_index(prim_num))    ! ...indices of primitive functions
     353          24 :       ALLOCATE (exponents(prim_num))      ! ...primitive exponents
     354          16 :       ALLOCATE (coefficients(prim_num))   ! ...contraction coefficients
     355          16 :       ALLOCATE (prim_factor(prim_num))    ! ...primitive normalization factors
     356             : 
     357           8 :       ishell = 0
     358           8 :       ipgf = 0
     359          34 :       DO iatom = 1, natoms
     360             :          ! get the qs_kind (index position in kind_set) for this atom (atomic_kind)
     361          26 :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     362             :          ! get the primary (orbital) basis set associated to this qs_kind
     363          26 :          CALL get_qs_kind(kind_set(ikind), basis_set=basis_set, basis_type="ORB")
     364             :          ! get the info from the basis set
     365             :          CALL get_gto_basis_set(basis_set, &
     366             :                                 nset=nset, &
     367             :                                 nshell=nshell, &
     368             :                                 npgf=npgf, &
     369             :                                 zet=zetas, &
     370             :                                 gcc=gcc, &
     371          26 :                                 l=l_shell_set)
     372             : 
     373         148 :          DO iset = 1, nset
     374         252 :             DO ishell_loc = 1, nshell(iset)
     375         138 :                ishell = ishell + 1
     376             : 
     377             :                ! nucleus_index array
     378         138 :                nucleus_index(ishell) = iatom
     379             : 
     380             :                ! shell_ang_mom array
     381         138 :                l = l_shell_set(ishell_loc, iset)
     382         138 :                shell_ang_mom(ishell) = l
     383             : 
     384             :                ! shell_index array
     385         512 :                shell_index(ipgf + 1:ipgf + npgf(iset)) = ishell
     386             : 
     387             :                ! exponents array
     388         512 :                exponents(ipgf + 1:ipgf + npgf(iset)) = zetas(1:npgf(iset), iset)
     389             : 
     390             :                ! compute on the fly the normalization factor as in normalise_gcc_orb
     391             :                ! and recover the original contraction coefficients to store them separately
     392         138 :                expzet = 0.25_dp*REAL(2*l + 3, dp)
     393         138 :                prefac = 2.0_dp**l*(2.0_dp/pi)**0.75_dp
     394         512 :                DO i = 1, npgf(iset)
     395         374 :                   gcca = gcc(i, ishell_loc, iset)
     396         374 :                   zeta = zetas(i, iset)
     397             : 
     398             :                   ! primitives normalization factors array
     399         374 :                   prim_factor(i + ipgf) = prefac*zeta**expzet
     400             : 
     401             :                   ! contractio coefficients array
     402         512 :                   coefficients(i + ipgf) = gcca/prim_factor(i + ipgf)
     403             :                END DO
     404             : 
     405         226 :                ipgf = ipgf + npgf(iset)
     406             :             END DO
     407             :          END DO
     408             :       END DO
     409             :       ! just a failsafe check
     410           8 :       CPASSERT(ishell == shell_num)
     411           8 :       CPASSERT(ipgf == prim_num)
     412             : 
     413           8 :       IF (ionode) THEN
     414           4 :          rc = trexio_write_basis_nucleus_index(f, nucleus_index)
     415           4 :          CALL trexio_error(rc)
     416             : 
     417           4 :          rc = trexio_write_basis_shell_ang_mom(f, shell_ang_mom)
     418           4 :          CALL trexio_error(rc)
     419             : 
     420             :          ! Normalization factors are shoved in the AO group
     421          12 :          ALLOCATE (shell_factor(shell_num))  ! 1-to-1 map bw shells and normalization factors
     422          73 :          shell_factor(:) = 1.0_dp
     423           4 :          rc = trexio_write_basis_shell_factor(f, shell_factor)
     424           4 :          CALL trexio_error(rc)
     425           4 :          DEALLOCATE (shell_factor)
     426             : 
     427             :          ! This is always 0 for Gaussian basis sets
     428          12 :          ALLOCATE (r_power(shell_num))       ! 1-to-1 map bw shells radial function powers
     429          73 :          r_power(:) = 0
     430           4 :          rc = trexio_write_basis_r_power(f, r_power)
     431           4 :          CALL trexio_error(rc)
     432           4 :          DEALLOCATE (r_power)
     433             : 
     434           4 :          rc = trexio_write_basis_shell_index(f, shell_index)
     435           4 :          CALL trexio_error(rc)
     436             : 
     437           4 :          rc = trexio_write_basis_exponent(f, exponents)
     438           4 :          CALL trexio_error(rc)
     439             : 
     440           4 :          rc = trexio_write_basis_coefficient(f, coefficients)
     441           4 :          CALL trexio_error(rc)
     442             : 
     443             :          ! Normalization factors are shoved in the AO group
     444           4 :          rc = trexio_write_basis_prim_factor(f, prim_factor)
     445           4 :          CALL trexio_error(rc)
     446             :       END IF
     447             : 
     448           8 :       DEALLOCATE (nucleus_index)
     449           8 :       DEALLOCATE (shell_index)
     450           8 :       DEALLOCATE (exponents)
     451           8 :       DEALLOCATE (coefficients)
     452           8 :       DEALLOCATE (prim_factor)
     453             :       ! shell_ang_mom is needed in the MO group, so will be deallocated there
     454             : 
     455             :       !========================================================================================!
     456             :       ! ECP group
     457             :       !========================================================================================!
     458           8 :       IF (ionode) THEN
     459           4 :          CALL get_qs_kind_set(kind_set, sgp_potential_present=sgp_potential_present)
     460             : 
     461             :          ! figure out whether we actually have ECP potentials
     462           4 :          ecp_num = 0
     463           4 :          IF (sgp_potential_present) THEN
     464           4 :             DO iatom = 1, natoms
     465             :                ! get the qs_kind (index position in kind_set) for this atom (atomic_kind)
     466           2 :                CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     467             :                ! get the the sgp_potential associated to this qs_kind
     468           2 :                CALL get_qs_kind(kind_set(ikind), sgp_potential=sgp_potential)
     469             : 
     470             :                ! get the info on the potential
     471           6 :                IF (ASSOCIATED(sgp_potential)) THEN
     472           2 :                   CALL get_potential(potential=sgp_potential, ecp_local=ecp_local, ecp_semi_local=ecp_semi_local)
     473           2 :                   IF (ecp_local) THEN
     474             :                      ! get number of local terms
     475           2 :                      CALL get_potential(potential=sgp_potential, nloc=nloc)
     476           2 :                      ecp_num = ecp_num + nloc
     477             :                   END IF
     478           2 :                   IF (ecp_semi_local) THEN
     479             :                      ! get number of semilocal terms
     480           2 :                      CALL get_potential(potential=sgp_potential, npot=npot)
     481          24 :                      ecp_num = ecp_num + SUM(npot)
     482             :                   END IF
     483             :                END IF
     484             :             END DO
     485             :          END IF
     486             : 
     487             :          ! if we have ECP potentials, populate the ECP group
     488           2 :          IF (ecp_num > 0) THEN
     489           6 :             ALLOCATE (z_core(natoms))
     490           4 :             ALLOCATE (max_ang_mom_plus_1(natoms))
     491           4 :             max_ang_mom_plus_1(:) = 0
     492             : 
     493           6 :             ALLOCATE (ang_mom(ecp_num))
     494           4 :             ALLOCATE (nucleus_index(ecp_num))
     495           6 :             ALLOCATE (exponents(ecp_num))
     496           4 :             ALLOCATE (coefficients(ecp_num))
     497           4 :             ALLOCATE (powers(ecp_num))
     498             : 
     499           4 :             iecp = 0
     500           4 :             DO iatom = 1, natoms
     501             :                ! get the qs_kind (index position in kind_set) for this atom (atomic_kind)
     502           2 :                CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind, z=z)
     503             :                ! get the the sgp_potential associated to this qs_kind
     504           2 :                CALL get_qs_kind(kind_set(ikind), sgp_potential=sgp_potential, zeff=zeff)
     505             : 
     506             :                ! number of core electrons removed by the ECP
     507           2 :                z_core(iatom) = z - INT(zeff)
     508             : 
     509             :                ! get the info on the potential
     510           4 :                IF (ASSOCIATED(sgp_potential)) THEN
     511           2 :                   CALL get_potential(potential=sgp_potential, ecp_local=ecp_local, ecp_semi_local=ecp_semi_local)
     512             : 
     513             :                   ! deal with the local part
     514           2 :                   IF (ecp_local) THEN
     515           2 :                      CALL get_potential(potential=sgp_potential, nloc=nloc, sl_lmax=sl_lmax)
     516           4 :                      ang_mom(iecp + 1:iecp + nloc) = sl_lmax + 1
     517           4 :                      nucleus_index(iecp + 1:iecp + nloc) = iatom
     518           4 :                      exponents(iecp + 1:iecp + nloc) = sgp_potential%bloc(1:nloc)
     519           4 :                      coefficients(iecp + 1:iecp + nloc) = sgp_potential%aloc(1:nloc)
     520           4 :                      powers(iecp + 1:iecp + nloc) = sgp_potential%nrloc(1:nloc) - 2
     521             :                      iecp = iecp + nloc
     522             :                   END IF
     523             : 
     524             :                   ! deal with the semilocal part
     525           2 :                   IF (ecp_semi_local) THEN
     526           2 :                      CALL get_potential(potential=sgp_potential, npot=npot, sl_lmax=sl_lmax)
     527           2 :                      max_ang_mom_plus_1(iatom) = sl_lmax + 1
     528             : 
     529           8 :                      DO sl_l = 0, sl_lmax
     530           6 :                         nsemiloc = npot(sl_l)
     531          16 :                         ang_mom(iecp + 1:iecp + nsemiloc) = sl_l
     532          16 :                         nucleus_index(iecp + 1:iecp + nsemiloc) = iatom
     533          16 :                         exponents(iecp + 1:iecp + nsemiloc) = sgp_potential%bpot(1:nsemiloc, sl_l)
     534          16 :                         coefficients(iecp + 1:iecp + nsemiloc) = sgp_potential%apot(1:nsemiloc, sl_l)
     535          16 :                         powers(iecp + 1:iecp + nsemiloc) = sgp_potential%nrpot(1:nsemiloc, sl_l) - 2
     536           8 :                         iecp = iecp + nsemiloc
     537             :                      END DO
     538             :                   END IF
     539             :                END IF
     540             :             END DO
     541             : 
     542             :             ! fail-safe check
     543           2 :             CPASSERT(iecp == ecp_num)
     544             : 
     545           2 :             rc = trexio_write_ecp_num(f, ecp_num)
     546           2 :             CALL trexio_error(rc)
     547             : 
     548           2 :             rc = trexio_write_ecp_z_core(f, z_core)
     549           2 :             CALL trexio_error(rc)
     550           2 :             DEALLOCATE (z_core)
     551             : 
     552           2 :             rc = trexio_write_ecp_max_ang_mom_plus_1(f, max_ang_mom_plus_1)
     553           2 :             CALL trexio_error(rc)
     554           2 :             DEALLOCATE (max_ang_mom_plus_1)
     555             : 
     556           2 :             rc = trexio_write_ecp_ang_mom(f, ang_mom)
     557           2 :             CALL trexio_error(rc)
     558           2 :             DEALLOCATE (ang_mom)
     559             : 
     560           2 :             rc = trexio_write_ecp_nucleus_index(f, nucleus_index)
     561           2 :             CALL trexio_error(rc)
     562           2 :             DEALLOCATE (nucleus_index)
     563             : 
     564           2 :             rc = trexio_write_ecp_exponent(f, exponents)
     565           2 :             CALL trexio_error(rc)
     566           2 :             DEALLOCATE (exponents)
     567             : 
     568           2 :             rc = trexio_write_ecp_coefficient(f, coefficients)
     569           2 :             CALL trexio_error(rc)
     570           2 :             DEALLOCATE (coefficients)
     571             : 
     572           2 :             rc = trexio_write_ecp_power(f, powers)
     573           2 :             CALL trexio_error(rc)
     574           2 :             DEALLOCATE (powers)
     575             :          END IF
     576             : 
     577             :       END IF ! ionode
     578             : 
     579             :       !========================================================================================!
     580             :       ! Grid group
     581             :       !========================================================================================!
     582             :       ! TODO
     583             : 
     584             :       !========================================================================================!
     585             :       ! AO group
     586             :       !========================================================================================!
     587           8 :       CALL get_qs_env(qs_env, qs_kind_set=kind_set)
     588           8 :       CALL get_qs_kind_set(kind_set, ncgf=ncgf, nsgf=nsgf)
     589             : 
     590           8 :       CALL section_vals_val_get(trexio_section, "CARTESIAN", l_val=save_cartesian)
     591           8 :       IF (save_cartesian) THEN
     592           4 :          ao_num = ncgf
     593             :       ELSE
     594           4 :          ao_num = nsgf
     595             :       END IF
     596             : 
     597           8 :       IF (ionode) THEN
     598           4 :          IF (save_cartesian) THEN
     599           2 :             rc = trexio_write_ao_cartesian(f, 1)
     600             :          ELSE
     601           2 :             rc = trexio_write_ao_cartesian(f, 0)
     602             :          END IF
     603           4 :          CALL trexio_error(rc)
     604             : 
     605           4 :          rc = trexio_write_ao_num(f, ao_num)
     606           4 :          CALL trexio_error(rc)
     607             :       END IF
     608             : 
     609             :       ! one-to-one mapping between AOs and ...
     610          24 :       ALLOCATE (ao_shell(ao_num))         ! ..shells
     611          24 :       ALLOCATE (ao_normalization(ao_num)) ! ..normalization factors
     612             : 
     613             :       ! we need to be consistent with the basis group on the shell indices
     614           8 :       ishell = 0  ! global shell index
     615           8 :       igf = 0     ! global AO index
     616          34 :       DO iatom = 1, natoms
     617             :          ! get the qs_kind (index position in kind_set) for this atom (atomic_kind)
     618          26 :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     619             :          ! get the primary (orbital) basis set associated to this qs_kind
     620          26 :          CALL get_qs_kind(kind_set(ikind), basis_set=basis_set, basis_type="ORB")
     621             :          ! get the info from the basis set
     622             :          CALL get_gto_basis_set(basis_set, &
     623             :                                 nset=nset, &
     624             :                                 nshell=nshell, &
     625             :                                 norm_cgf=norm_cgf, &
     626             :                                 ncgf=ncgf, &
     627             :                                 nsgf=nsgf, &
     628          26 :                                 l=l_shell_set)
     629             : 
     630          26 :          icgf = 0
     631         114 :          DO iset = 1, nset
     632         252 :             DO ishell_loc = 1, nshell(iset)
     633             :                ! global shell index
     634         138 :                ishell = ishell + 1
     635             :                ! angular momentum l of this shell
     636         138 :                lshell = l_shell_set(ishell_loc, iset)
     637             : 
     638             :                ! number of AOs in this shell
     639         138 :                IF (save_cartesian) THEN
     640          34 :                   ngf_shell = nco(lshell)
     641             :                ELSE
     642         104 :                   ngf_shell = nso(lshell)
     643             :                END IF
     644             : 
     645             :                ! one-to-one mapping between AOs and shells
     646         524 :                ao_shell(igf + 1:igf + ngf_shell) = ishell
     647             : 
     648             :                ! one-to-one mapping between AOs and normalization factors
     649         138 :                IF (save_cartesian) THEN
     650         136 :                   ao_normalization(igf + 1:igf + ngf_shell) = norm_cgf(icgf + 1:icgf + ngf_shell)
     651             :                ELSE
     652             :                   ! allocate some temporary arrays
     653         416 :                   ALLOCATE (diag_ncgf(nco(lshell), nco(lshell)))
     654         416 :                   ALLOCATE (diag_nsgf(nso(lshell), nso(lshell)))
     655         416 :                   ALLOCATE (temp(nso(lshell), nco(lshell)))
     656        1808 :                   diag_ncgf = 0.0_dp
     657        1436 :                   diag_nsgf = 0.0_dp
     658        1616 :                   temp = 0.0_dp
     659             : 
     660         416 :                   DO i = 1, nco(lshell)
     661         416 :                      diag_ncgf(i, i) = norm_cgf(icgf + i)
     662             :                   END DO
     663             : 
     664             :                   ! transform the normalization factors from Cartesian to solid harmonics
     665       42200 :                   temp(:, :) = MATMUL(orbtramat(lshell)%c2s, diag_ncgf)
     666       24520 :                   diag_nsgf(:, :) = MATMUL(temp, TRANSPOSE(orbtramat(lshell)%s2c))
     667         388 :                   DO i = 1, nso(lshell)
     668         388 :                      ao_normalization(igf + i) = diag_nsgf(i, i)
     669             :                   END DO
     670             : 
     671         104 :                   DEALLOCATE (diag_ncgf)
     672         104 :                   DEALLOCATE (diag_nsgf)
     673         104 :                   DEALLOCATE (temp)
     674             :                END IF
     675             : 
     676         138 :                igf = igf + ngf_shell
     677         226 :                icgf = icgf + nco(lshell)
     678             :             END DO
     679             :          END DO
     680             :          ! just a failsafe check
     681          86 :          CPASSERT(icgf == ncgf)
     682             :       END DO
     683             : 
     684           8 :       IF (ionode) THEN
     685           4 :          rc = trexio_write_ao_shell(f, ao_shell)
     686           4 :          CALL trexio_error(rc)
     687             : 
     688           4 :          rc = trexio_write_ao_normalization(f, ao_normalization)
     689           4 :          CALL trexio_error(rc)
     690             :       END IF
     691             : 
     692           8 :       DEALLOCATE (ao_shell)
     693           8 :       DEALLOCATE (ao_normalization)
     694             : 
     695             :       !========================================================================================!
     696             :       ! MO group
     697             :       !========================================================================================!
     698             :       CALL get_qs_env(qs_env, do_kpoints=do_kpoints, kpoints=kpoints, dft_control=dft_control, &
     699           8 :                       particle_set=particle_set, qs_kind_set=kind_set, blacs_env=blacs_env)
     700           8 :       nspins = dft_control%nspins
     701           8 :       CALL get_qs_kind_set(kind_set, nsgf=nsgf)
     702           8 :       nmo_spin = 0
     703             : 
     704             :       ! figure out that total number of MOs
     705           8 :       mo_num = 0
     706           8 :       IF (do_kpoints) THEN
     707           2 :          CALL get_kpoint_info(kpoints, kp_env=kp_env, nkp=nkp, use_real_wfn=use_real_wfn)
     708           2 :          CALL get_kpoint_env(kp_env(1)%kpoint_env, mos=mos_kp)
     709           4 :          DO ispin = 1, nspins
     710           2 :             CALL get_mo_set(mos_kp(1, ispin), nmo=nmo)
     711           4 :             nmo_spin(ispin) = nmo
     712             :          END DO
     713           6 :          mo_num = nkp*SUM(nmo_spin)
     714             : 
     715             :          ! we create a distributed fm matrix to gather the MOs from everywhere (in sph basis)
     716             :          CALL cp_fm_struct_create(fm_struct, para_env=para_env, context=blacs_env, &
     717           2 :                                   nrow_global=nsgf, ncol_global=mo_num)
     718           2 :          CALL cp_fm_create(fm_mo_coeff, fm_struct)
     719           2 :          CALL cp_fm_set_all(fm_mo_coeff, 0.0_dp)
     720           2 :          IF (.NOT. use_real_wfn) THEN
     721           2 :             CALL cp_fm_create(fm_mo_coeff_im, fm_struct)
     722           2 :             CALL cp_fm_set_all(fm_mo_coeff_im, 0.0_dp)
     723             :          END IF
     724           2 :          CALL cp_fm_struct_release(fm_struct)
     725             :       ELSE
     726           6 :          CALL get_qs_env(qs_env, mos=mos)
     727          14 :          DO ispin = 1, nspins
     728           8 :             CALL get_mo_set(mos(ispin), nmo=nmo)
     729          14 :             nmo_spin(ispin) = nmo
     730             :          END DO
     731          18 :          mo_num = SUM(nmo_spin)
     732             :       END IF
     733             : 
     734             :       ! allocate all the arrays
     735          32 :       ALLOCATE (mo_coefficient(ao_num, mo_num))
     736       33432 :       mo_coefficient(:, :) = 0.0_dp
     737          24 :       ALLOCATE (mo_energy(mo_num))
     738         436 :       mo_energy(:) = 0.0_dp
     739          16 :       ALLOCATE (mo_occupation(mo_num))
     740         436 :       mo_occupation(:) = 0.0_dp
     741          24 :       ALLOCATE (mo_spin(mo_num))
     742         436 :       mo_spin(:) = 0
     743             :       ! extra arrays for kpoints
     744           8 :       IF (do_kpoints) THEN
     745           6 :          ALLOCATE (mo_coefficient_im(ao_num, mo_num))
     746       26882 :          mo_coefficient_im(:, :) = 0.0_dp
     747           4 :          ALLOCATE (mo_kpoint(mo_num))
     748         258 :          mo_kpoint(:) = 0
     749             :       END IF
     750             : 
     751             :       ! in case of kpoints, we do this in 2 steps:
     752             :       ! 1. we gather the MOs of each kpt and pipe them into a single large distributed fm matrix;
     753             :       ! 2. we possibly transform the MOs of each kpt to Cartesian AOs and write them in the single large local array;
     754           8 :       IF (do_kpoints) THEN
     755           2 :          CALL get_kpoint_info(kpoints, kp_env=kp_env, nkp=nkp, kp_range=kp_range)
     756             : 
     757           4 :          DO ispin = 1, nspins
     758          20 :             DO ikp = 1, nkp
     759          16 :                nmo = nmo_spin(ispin)
     760             :                ! global index to store the MOs
     761          16 :                imo = (ikp - 1)*nmo + (ispin - 1)*nmo_spin(1)*nkp
     762             : 
     763             :                ! do I have this kpoint on this rank?
     764          18 :                IF (ikp >= kp_range(1) .AND. ikp <= kp_range(2)) THEN
     765          16 :                   ikp_loc = ikp - kp_range(1) + 1
     766             :                   ! get the mo set for this kpoint
     767          16 :                   CALL get_kpoint_env(kp_env(ikp_loc)%kpoint_env, mos=mos_kp)
     768             : 
     769             :                   ! if MOs are stored with dbcsr, copy them to fm
     770          16 :                   IF (mos_kp(1, ispin)%use_mo_coeff_b) THEN
     771           0 :                      CALL copy_dbcsr_to_fm(mos_kp(1, ispin)%mo_coeff_b, mos_kp(1, ispin)%mo_coeff)
     772             :                   END IF
     773             :                   ! copy real part of MO coefficients to large distributed fm matrix
     774             :                   CALL cp_fm_to_fm_submat_general(mos_kp(1, ispin)%mo_coeff, fm_mo_coeff, &
     775          16 :                                                   nsgf, nmo, 1, 1, 1, imo + 1, blacs_env)
     776             : 
     777             :                   ! copy MO energies to local arrays
     778         272 :                   mo_energy(imo + 1:imo + nmo) = mos_kp(1, ispin)%eigenvalues(1:nmo)
     779             : 
     780             :                   ! copy MO occupations to local arrays
     781         272 :                   mo_occupation(imo + 1:imo + nmo) = mos_kp(1, ispin)%occupation_numbers(1:nmo)
     782             : 
     783             :                   ! same for the imaginary part of MO coefficients
     784          16 :                   IF (.NOT. use_real_wfn) THEN
     785          16 :                      IF (mos_kp(2, ispin)%use_mo_coeff_b) THEN
     786           0 :                         CALL copy_dbcsr_to_fm(mos_kp(2, ispin)%mo_coeff_b, mos_kp(2, ispin)%mo_coeff)
     787             :                      END IF
     788             :                      CALL cp_fm_to_fm_submat_general(mos_kp(2, ispin)%mo_coeff, fm_mo_coeff_im, &
     789          16 :                                                      nsgf, nmo, 1, 1, 1, imo + 1, blacs_env)
     790             :                   END IF
     791             :                ELSE
     792             :                   ! call with a dummy fm for receiving the data
     793             :                   CALL cp_fm_to_fm_submat_general(fm_dummy, fm_mo_coeff, &
     794           0 :                                                   nsgf, nmo, 1, 1, 1, imo + 1, blacs_env)
     795           0 :                   IF (.NOT. use_real_wfn) THEN
     796             :                      CALL cp_fm_to_fm_submat_general(fm_dummy, fm_mo_coeff_im, &
     797           0 :                                                      nsgf, nmo, 1, 1, 1, imo + 1, blacs_env)
     798             :                   END IF
     799             :                END IF
     800             :             END DO
     801             :          END DO
     802             :       END IF
     803             : 
     804             :       ! reduce MO energies and occupations to the master node
     805           8 :       IF (do_kpoints) THEN
     806           2 :          CALL get_kpoint_info(kpoints, para_env_inter_kp=para_env_inter_kp)
     807           2 :          CALL para_env_inter_kp%sum(mo_energy)
     808           2 :          CALL para_env_inter_kp%sum(mo_occupation)
     809             :       END IF
     810             : 
     811             :       ! second step: here we actually put everything in the local arrays for writing to trexio
     812          18 :       DO ispin = 1, nspins
     813             :          ! get number of MOs for this spin
     814          10 :          nmo = nmo_spin(ispin)
     815             :          ! allocate local temp array to transform the MOs of each kpoint/spin
     816          40 :          ALLOCATE (mos_sgf(nsgf, nmo))
     817             : 
     818          10 :          IF (do_kpoints) THEN
     819          18 :             DO ikp = 1, nkp
     820             :                ! global index to store the MOs
     821          16 :                imo = (ikp - 1)*nmo + (ispin - 1)*nmo_spin(1)*nkp
     822             : 
     823             :                ! store kpoint index
     824         272 :                mo_kpoint(imo + 1:imo + nmo) = ikp
     825             :                ! store the MO spins
     826         272 :                mo_spin(imo + 1:imo + nmo) = ispin - 1
     827             : 
     828             :                ! transform and store the MO coefficients
     829          16 :                CALL cp_fm_get_submatrix(fm_mo_coeff, mos_sgf, 1, imo + 1, nsgf, nmo)
     830          16 :                IF (save_cartesian) THEN
     831           0 :                   CALL spherical_to_cartesian_mo(mos_sgf, particle_set, kind_set, mo_coefficient(:, imo + 1:imo + nmo))
     832             :                ELSE
     833             :                   ! we have to reorder the MOs since CP2K and TREXIO have different conventions
     834             :                   ! from m = -l, -l+1, ..., 0, ..., l-1, l   of CP2K
     835             :                   ! to   m =  0, +1, -1, +2, -2, ..., +l, -l of TREXIO
     836          16 :                   i = 0
     837         656 :                   DO ishell = 1, shell_num
     838         640 :                      l = shell_ang_mom(ishell)
     839        2304 :                      DO k = 1, 2*l + 1
     840             :                         ! map from running index k: 1...2l+1 to magnetic quantum number m in TREXIO convention
     841        1664 :                         m = (-1)**k*FLOOR(REAL(k, KIND=dp)/2.0_dp)
     842       28928 :                         mo_coefficient(i + k, imo + 1:imo + nmo) = mos_sgf(i + l + m + 1, :)
     843             :                      END DO
     844         656 :                      i = i + 2*l + 1
     845             :                   END DO
     846          16 :                   CPASSERT(i == nsgf)
     847             :                END IF
     848             : 
     849             :                ! we have to do it for the imaginary part as well
     850          18 :                IF (.NOT. use_real_wfn) THEN
     851          16 :                   CALL cp_fm_get_submatrix(fm_mo_coeff_im, mos_sgf, 1, imo + 1, nsgf, nmo)
     852          16 :                   IF (save_cartesian) THEN
     853           0 :                      CALL spherical_to_cartesian_mo(mos_sgf, particle_set, kind_set, mo_coefficient_im(:, imo + 1:imo + nmo))
     854             :                   ELSE
     855             :                      ! we have to reorder the MOs since CP2K and TREXIO have different conventions
     856             :                      ! from m = -l, -l+1, ..., 0, ..., l-1, l   of CP2K
     857             :                      ! to   m =  0, +1, -1, +2, -2, ..., +l, -l of TREXIO
     858          16 :                      i = 0
     859         656 :                      DO ishell = 1, shell_num
     860         640 :                         l = shell_ang_mom(ishell)
     861        2304 :                         DO k = 1, 2*l + 1
     862             :                            ! map from running index k: 1...2l+1 to magnetic quantum number m in TREXIO convention
     863        1664 :                            m = (-1)**k*FLOOR(REAL(k, KIND=dp)/2.0_dp)
     864       28928 :                            mo_coefficient_im(i + k, imo + 1:imo + nmo) = mos_sgf(i + l + m + 1, :)
     865             :                         END DO
     866         656 :                         i = i + 2*l + 1
     867             :                      END DO
     868          16 :                      CPASSERT(i == nsgf)
     869             :                   END IF
     870             :                END IF
     871             :             END DO
     872             :          ELSE ! no k-points
     873             :             ! global index to store the MOs
     874           8 :             imo = (ispin - 1)*nmo_spin(1)
     875             :             ! store the MO energies
     876         180 :             mo_energy(imo + 1:imo + nmo) = mos(ispin)%eigenvalues
     877             :             ! store the MO occupations
     878         180 :             mo_occupation(imo + 1:imo + nmo) = mos(ispin)%occupation_numbers
     879             :             ! store the MO spins
     880         180 :             mo_spin(imo + 1:imo + nmo) = ispin - 1
     881             : 
     882             :             ! check if we are using the dbcsr mo_coeff and copy them to fm if needed
     883           8 :             IF (mos(ispin)%use_mo_coeff_b) CALL copy_dbcsr_to_fm(mos(ispin)%mo_coeff_b, mos(ispin)%mo_coeff)
     884             : 
     885             :             ! allocate a normal fortran array to store the spherical MO coefficients
     886           8 :             CALL cp_fm_get_submatrix(mos(ispin)%mo_coeff, mos_sgf)
     887             : 
     888           8 :             IF (save_cartesian) THEN
     889           6 :                CALL spherical_to_cartesian_mo(mos_sgf, particle_set, kind_set, mo_coefficient(:, imo + 1:imo + nmo))
     890             :             ELSE
     891             :                ! we have to reorder the MOs since CP2K and TREXIO have different conventions
     892             :                ! from m = -l, -l+1, ..., 0, ..., l-1, l   of CP2K
     893             :                ! to   m =  0, +1, -1, +2, -2, ..., +l, -l of TREXIO
     894           2 :                i = 0
     895          26 :                DO ishell = 1, shell_num
     896          24 :                   l = shell_ang_mom(ishell)
     897         100 :                   DO k = 1, 2*l + 1
     898             :                      ! map from running index k: 1...2l+1 to magnetic quantum number m in TREXIO convention
     899          76 :                      m = (-1)**k*FLOOR(REAL(k, KIND=dp)/2.0_dp)
     900        2988 :                      mo_coefficient(i + k, imo + 1:imo + nmo) = mos_sgf(i + l + m + 1, :)
     901             :                   END DO
     902          26 :                   i = i + 2*l + 1
     903             :                END DO
     904           2 :                CPASSERT(i == nsgf)
     905             :             END IF
     906             :          END IF
     907             : 
     908          18 :          DEALLOCATE (mos_sgf)
     909             :       END DO
     910             : 
     911           8 :       IF (ionode) THEN
     912           4 :          rc = trexio_write_mo_type(f, 'Canonical', LEN_TRIM('Canonical') + 1)
     913           4 :          CALL trexio_error(rc)
     914             : 
     915           4 :          rc = trexio_write_mo_num(f, mo_num)
     916           4 :          CALL trexio_error(rc)
     917             : 
     918           4 :          rc = trexio_write_mo_coefficient(f, mo_coefficient)
     919           4 :          CALL trexio_error(rc)
     920             : 
     921           4 :          rc = trexio_write_mo_energy(f, mo_energy)
     922           4 :          CALL trexio_error(rc)
     923             : 
     924           4 :          rc = trexio_write_mo_occupation(f, mo_occupation)
     925           4 :          CALL trexio_error(rc)
     926             : 
     927           4 :          rc = trexio_write_mo_spin(f, mo_spin)
     928           4 :          CALL trexio_error(rc)
     929             : 
     930           4 :          IF (do_kpoints) THEN
     931           1 :             rc = trexio_write_mo_coefficient_im(f, mo_coefficient_im)
     932           1 :             CALL trexio_error(rc)
     933             : 
     934           1 :             rc = trexio_write_mo_k_point(f, mo_kpoint)
     935           1 :             CALL trexio_error(rc)
     936             :          END IF
     937             :       END IF
     938             : 
     939           8 :       DEALLOCATE (shell_ang_mom)
     940           8 :       DEALLOCATE (mo_coefficient)
     941           8 :       DEALLOCATE (mo_energy)
     942           8 :       DEALLOCATE (mo_occupation)
     943           8 :       DEALLOCATE (mo_spin)
     944           8 :       IF (do_kpoints) THEN
     945           2 :          DEALLOCATE (mo_coefficient_im)
     946           2 :          DEALLOCATE (mo_kpoint)
     947           2 :          CALL cp_fm_release(fm_mo_coeff)
     948           2 :          CALL cp_fm_release(fm_mo_coeff_im)
     949             :       END IF
     950             : 
     951             :       !========================================================================================!
     952             :       ! RDM group
     953             :       !========================================================================================!
     954             :       !TODO
     955             : 
     956             :       !========================================================================================!
     957             :       ! Close the TREXIO file
     958             :       !========================================================================================!
     959           8 :       IF (ionode) THEN
     960           4 :          rc = trexio_close(f)
     961           4 :          CALL trexio_error(rc)
     962             :       END IF
     963             : #else
     964             :       MARK_USED(qs_env)
     965             :       MARK_USED(trexio_section)
     966             :       CPWARN('TREXIO support has not been enabled in this build.')
     967             : #endif
     968           8 :       CALL timestop(handle)
     969             : 
     970          48 :    END SUBROUTINE write_trexio
     971             : 
     972             : #ifdef __TREXIO
     973             : ! **************************************************************************************************
     974             : !> \brief Handles TREXIO errors
     975             : !> \param rc the TREXIO return code
     976             : ! **************************************************************************************************
     977         195 :    SUBROUTINE trexio_error(rc)
     978             :       INTEGER(trexio_exit_code), INTENT(IN)              :: rc
     979             : 
     980             :       CHARACTER(LEN=128)                                 :: err_msg
     981             : 
     982         195 :       IF (rc /= TREXIO_SUCCESS) THEN
     983           0 :          CALL trexio_string_of_error(rc, err_msg)
     984           0 :          CPABORT('TREXIO Error: '//TRIM(err_msg))
     985             :       END IF
     986             : 
     987         195 :    END SUBROUTINE trexio_error
     988             : 
     989             : ! **************************************************************************************************
     990             : !> \brief Computes the nuclear repulsion energy of a molecular system
     991             : !> \param particle_set the set of particles in the system
     992             : !> \param kind_set the set of qs_kinds in the system
     993             : !> \param e_nn the nuclear repulsion energy
     994             : ! **************************************************************************************************
     995           2 :    SUBROUTINE nuclear_repulsion_energy(particle_set, kind_set, e_nn)
     996             :       TYPE(particle_type), DIMENSION(:), INTENT(IN), &
     997             :          POINTER                                         :: particle_set
     998             :       TYPE(qs_kind_type), DIMENSION(:), INTENT(IN), &
     999             :          POINTER                                         :: kind_set
    1000             :       REAL(KIND=dp), INTENT(OUT)                         :: e_nn
    1001             : 
    1002             :       INTEGER                                            :: i, ikind, j, jkind, natoms
    1003             :       REAL(KIND=dp)                                      :: r_ij, zeff_i, zeff_j
    1004             : 
    1005           2 :       natoms = SIZE(particle_set)
    1006           2 :       e_nn = 0.0_dp
    1007           4 :       DO i = 1, natoms
    1008           2 :          CALL get_atomic_kind(particle_set(i)%atomic_kind, kind_number=ikind)
    1009           2 :          CALL get_qs_kind(kind_set(ikind), zeff=zeff_i)
    1010           4 :          DO j = i + 1, natoms
    1011           0 :             r_ij = NORM2(particle_set(i)%r - particle_set(j)%r)
    1012             : 
    1013           0 :             CALL get_atomic_kind(particle_set(j)%atomic_kind, kind_number=jkind)
    1014           0 :             CALL get_qs_kind(kind_set(jkind), zeff=zeff_j)
    1015             : 
    1016           2 :             e_nn = e_nn + zeff_i*zeff_j/r_ij
    1017             :          END DO
    1018             :       END DO
    1019             : 
    1020           2 :    END SUBROUTINE nuclear_repulsion_energy
    1021             : 
    1022             : ! **************************************************************************************************
    1023             : !> \brief Computes a spherical to cartesian MO transformation (solid harmonics in reality)
    1024             : !> \param mos_sgf the MO coefficients in spherical AO basis
    1025             : !> \param particle_set the set of particles in the system
    1026             : !> \param qs_kind_set the set of qs_kinds in the system
    1027             : !> \param mos_cgf the transformed MO coefficients in Cartesian AO basis
    1028             : ! **************************************************************************************************
    1029           6 :    SUBROUTINE spherical_to_cartesian_mo(mos_sgf, particle_set, qs_kind_set, mos_cgf)
    1030             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: mos_sgf
    1031             :       TYPE(particle_type), DIMENSION(:), INTENT(IN), &
    1032             :          POINTER                                         :: particle_set
    1033             :       TYPE(qs_kind_type), DIMENSION(:), INTENT(IN), &
    1034             :          POINTER                                         :: qs_kind_set
    1035             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: mos_cgf
    1036             : 
    1037             :       INTEGER                                            :: iatom, icgf, ikind, iset, isgf, ishell, &
    1038             :                                                             lshell, ncgf, nmo, nset, nsgf
    1039           6 :       INTEGER, DIMENSION(:), POINTER                     :: nshell
    1040           6 :       INTEGER, DIMENSION(:, :), POINTER                  :: l
    1041             :       TYPE(gto_basis_set_type), POINTER                  :: orb_basis_set
    1042             : 
    1043           6 :       CALL get_qs_kind_set(qs_kind_set, ncgf=ncgf, nsgf=nsgf)
    1044             : 
    1045        3586 :       mos_cgf = 0.0_dp
    1046           6 :       nmo = SIZE(mos_sgf, 2)
    1047             : 
    1048             :       ! Transform spherical MOs to Cartesian MOs
    1049           6 :       icgf = 1
    1050           6 :       isgf = 1
    1051          20 :       DO iatom = 1, SIZE(particle_set)
    1052          14 :          NULLIFY (orb_basis_set)
    1053          14 :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
    1054          14 :          CALL get_qs_kind(qs_kind_set(ikind), basis_set=orb_basis_set)
    1055             : 
    1056          34 :          IF (ASSOCIATED(orb_basis_set)) THEN
    1057             :             CALL get_gto_basis_set(gto_basis_set=orb_basis_set, &
    1058             :                                    nset=nset, &
    1059             :                                    nshell=nshell, &
    1060          14 :                                    l=l)
    1061          54 :             DO iset = 1, nset
    1062          98 :                DO ishell = 1, nshell(iset)
    1063          44 :                   lshell = l(ishell, iset)
    1064             :                   CALL dgemm("T", "N", nco(lshell), nmo, nso(lshell), 1.0_dp, &
    1065             :                              orbtramat(lshell)%c2s, nso(lshell), &
    1066             :                              mos_sgf(isgf, 1), nsgf, 0.0_dp, &
    1067          44 :                              mos_cgf(icgf, 1), ncgf)
    1068          44 :                   icgf = icgf + nco(lshell)
    1069          84 :                   isgf = isgf + nso(lshell)
    1070             :                END DO
    1071             :             END DO
    1072             :          ELSE
    1073             :             ! assume atom without basis set
    1074           0 :             CPABORT("Unknown basis set type")
    1075             :          END IF
    1076             :       END DO ! iatom
    1077             : 
    1078           6 :    END SUBROUTINE spherical_to_cartesian_mo
    1079             : #endif
    1080             : 
    1081             : END MODULE trexio_utils

Generated by: LCOV version 1.15